package com.bestcem.xm.survey.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.http.HtmlUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bestcem.xm.common.core.exception.BusinessException;
import com.bestcem.xm.component.mybatis.base.ResultPage;
import com.bestcem.xm.component.mybatis.service.impl.XmServiceImpl;
import com.bestcem.xm.componet.redis.service.RedisService;
import com.bestcem.xm.qdes.grpc.v1.services.Project;
import com.bestcem.xm.qdes.grpc.v1.services.QStruct;
import com.bestcem.xm.qdes.grpc.v1.services.Question;
import com.bestcem.xm.qdes.grpc.v1.services.Quota;
import com.bestcem.xm.survey.controller.vo.*;
import com.bestcem.xm.survey.convert.SurveyConvertMapper;
import com.bestcem.xm.survey.entity.*;
import com.bestcem.xm.survey.grpc.client.QdesGrpcClient;
import com.bestcem.xm.survey.mapper.*;
import com.bestcem.xm.survey.service.SurveyQuotaConditionService;
import com.bestcem.xm.survey.util.PageUtil;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.bestcem.xm.qdes.grpc.v1.services.QuotaQuotaType.QUOTA_QUOTA_TYPE_CROSS;
import static com.bestcem.xm.survey.grpc.v1.services.RespondentDataStatus.RESPONDENT_DATA_STATUS_FORMAL;
import static com.bestcem.xm.survey.grpc.v1.services.RespondentStatus.RESPONDENT_STATUS_FINISH;

/**
 * @author guita
 * @description 针对表【ds_survey_quotacondition】的数据库操作Service实现
 * @createDate 2023-01-09 15:15:37
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SurveyQuotaConditionServiceImpl extends XmServiceImpl<SurveyQuotaConditionMapper, SurveyQuotaCondition>
        implements SurveyQuotaConditionService {
    private @NonNull SurveyQuotaConditionQgidListMapper surveyQuotaConditionQgidListMapper;
    private @NonNull SurveyQuotaCheckItemMapper surveyQuotaCheckItemMapper;
    private @NonNull SurveyQuotaCheckItemOptionListMapper surveyQuotaCheckItemOptionListMapper;
    private @NonNull SurveyQuotaCheckItemOptionCombiesMapper surveyQuotaCheckItemOptionCombiesMapper;
    private @NonNull SurveyConvertMapper surveyConvertMapper;
    private @NonNull SurveyRespondentMapper surveyRespondentMapper;
    private @NonNull SurveyRespondentDataMapper surveyRespondentDataMapper;
    private @NonNull SurveyRespondentQuotaCheckItemMapper surveyRespondentQuotaCheckItemMapper;
    private @NonNull SurveyQuotaConditionQgidListMapper surveyQuotaConditionGidListMapper;

    private @NonNull QdesGrpcClient qdesGrpcClient;
    private @NonNull RedisService redisService;

    @Override
    public JSONObject getCheckpointsWithQuotas(String project_id) {
        log.info("getCheckpointsWithQuotas project_id={}", project_id);
        List<SurveyQuotaCondition> quota_conditions = baseMapper.selectList(
                Wrappers.<SurveyQuotaCondition>lambdaQuery()
                        .eq(SurveyQuotaCondition::getProjectId, project_id));

        JSONObject checkpoint_dict = new JSONObject();
        quota_conditions.forEach(quota_condition -> {
            //[{"cid": "Q152", "gid": "3130", "title": "单选题"}]
            JSONArray checkpoints = JSONArray.parseArray(quota_condition.getCheckpoints());
            String quota_id = quota_condition.getQuotaId();
            checkpoints.forEach(tmp -> {
                JSONObject checkpoint = (JSONObject) tmp;
                String gid = checkpoint.getString("gid");
                Set<String> quota_ids;
                if (ObjectUtils.isEmpty(checkpoint_dict.getJSONArray(gid))) {
                    quota_ids = new HashSet<>();
                } else {
                    quota_ids = new HashSet<>(checkpoint_dict.getJSONArray(gid).toJavaList(String.class));
                }
                quota_ids.add(quota_id);
                if (!ObjectUtils.isEmpty(quota_ids)) {
                    checkpoint_dict.put(gid, quota_ids);
                }
            });
        });
        return checkpoint_dict;
    }

    @Override
    public SurveyQuotaConditionVo getById(String id) {
        SurveyQuotaCondition surveyQuotaCondition = baseMapper.selectById(id);
        log.info("The id is {}", id);
        return setSurveyQuotaConditionVo(surveyQuotaCondition, true);
    }

    @Override
    public List<SurveyQuotaConditionVo> getAll(String projectId, String quotaId) {
        LambdaQueryWrapper<SurveyQuotaCondition> defaultWrapper = Wrappers.lambdaQuery();
        defaultWrapper.eq(SurveyQuotaCondition::getQuotaId, quotaId);
        List<SurveyQuotaCondition> surveyQuotaConditions = baseMapper.selectList(defaultWrapper);
        List<SurveyQuotaConditionVo> surveyQuotaConditionVos = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(surveyQuotaConditions)) {
            surveyQuotaConditions.forEach(surveyQuotaCondition -> {
                SurveyQuotaConditionVo surveyQuotaConditionVo = setSurveyQuotaConditionVo(surveyQuotaCondition, true);
                surveyQuotaConditionVos.add(surveyQuotaConditionVo);
            });
        }
        return surveyQuotaConditionVos;
    }

    @Override
    public List<SurveyQuotaConditionVo> getByIds(List<String> ids) {
        List<SurveyQuotaConditionVo> surveyQuotaConditionVos = new ArrayList<>();
        if (CollectionUtils.isEmpty(ids)) {
            return surveyQuotaConditionVos;
        }
        LambdaQueryWrapper<SurveyQuotaCondition> defaultWrapper = Wrappers.lambdaQuery();
        defaultWrapper.in(SurveyQuotaCondition::getId, ids);
        List<SurveyQuotaCondition> surveyQuotaConditions = baseMapper.selectList(defaultWrapper);
        if (CollectionUtils.isNotEmpty(surveyQuotaConditions)) {
            surveyQuotaConditions.forEach(surveyQuotaCondition -> {
                SurveyQuotaConditionVo surveyQuotaConditionVo = setSurveyQuotaConditionVo(surveyQuotaCondition, true);
                surveyQuotaConditionVos.add(surveyQuotaConditionVo);
            });
        }
        return surveyQuotaConditionVos;
    }

    @Override
    public ResultPage<JSONObject> getByPage(Page<SurveyQuotaCondition> page, String projectId, String quotaId) {
        // 注意：该接口返回的顺序会影响批量设置配额的顺序，两者需要保持一致
        LambdaQueryWrapper<SurveyQuotaCondition> defaultWrapper = Wrappers.lambdaQuery();
        defaultWrapper.eq(SurveyQuotaCondition::getQuotaId, quotaId)
                .orderByAsc(SurveyQuotaCondition::getCreatedDt);
        Page<SurveyQuotaCondition> pageResult = baseMapper.selectPage(page, defaultWrapper);
        List<SurveyQuotaCondition> records = pageResult.getRecords();
        List<JSONObject> surveyQuotaConditionVos = new ArrayList<>();
        if (CollectionUtils.isEmpty(records)) {
            return PageUtil.getResultPage(pageResult.getCurrent(), pageResult.getTotal(), surveyQuotaConditionVos);
        }
        records.forEach(surveyQuotaCondition -> {
            SurveyQuotaConditionVo surveyQuotaConditionVo = setSurveyQuotaConditionVo(surveyQuotaCondition, true);
            JSONObject surveyQuotaConditionJs = JSONObject.parseObject(JSONObject.toJSONString(surveyQuotaConditionVo));
            surveyQuotaConditionJs.put("checkpoints", JSONArray.parseArray(surveyQuotaConditionVo.getCheckpoints()));
            // _set_item
            List<SurveyQuotaCheckItemVo> quotaCheckitemList = surveyQuotaConditionVo.getQuotaCheckitemList();
            if (CollectionUtils.isNotEmpty(quotaCheckitemList)) {
                surveyQuotaConditionJs.put("quota_checkitem_list", quotaCheckitemList.stream().map(SurveyQuotaCheckItemVo::getId).collect(Collectors.toList()));
            }
            List<SurveyQuotaConditionQgidList> qgidList = surveyQuotaConditionVo.getQgidList();
            if (CollectionUtils.isNotEmpty(qgidList)) {
                surveyQuotaConditionJs.put("qgid_list", qgidList.stream().map(SurveyQuotaConditionQgidList::getQgid).collect(Collectors.toList()));
            }
            surveyQuotaConditionVos.add(surveyQuotaConditionJs);
        });
        return PageUtil.getResultPage(page.getCurrent(), pageResult.getTotal(), surveyQuotaConditionVos);
    }

    @Override
    public JSONObject getNormalQuotaUsedOptions(String projectId) {
        // 1. 根据 normal_quota_ids 找到 quota_condition_ids;
        // 2. 根据 quota_condition_ids, 从 quota_checkitem中 找到option_list;
        // 获取quota
        log.info("Now to getNormalQuotaUsedOptions, projectId = {}", projectId);
        List<Quota> quotaList = qdesGrpcClient.getQuotaList(projectId, 0);
        List<String> normalQuotaIds = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(quotaList)) {
            normalQuotaIds = quotaList.stream().map(Quota::getId).collect(Collectors.toList());
        }
        List<SurveyQuotaCondition> quotaConditions = new ArrayList<>();
        LambdaQueryWrapper<SurveyQuotaCondition> defaultWrapper = Wrappers.lambdaQuery();
        defaultWrapper.eq(SurveyQuotaCondition::getProjectId, projectId);
        if (CollectionUtils.isNotEmpty(normalQuotaIds)) {
            defaultWrapper.in(SurveyQuotaCondition::getQuotaId, normalQuotaIds);
            quotaConditions = baseMapper.selectList(defaultWrapper);
        }
        if (CollectionUtils.isEmpty(quotaConditions)) {
            return new JSONObject();
        }
        List<SurveyQuotaConditionVo> quotaConditionVos = new ArrayList<>();
        quotaConditions.forEach(quotaCondition -> {
            SurveyQuotaConditionVo quotaConditionVo = setSurveyQuotaConditionVo(quotaCondition, false);
            quotaConditionVos.add(quotaConditionVo);
        });

        // 每个问题对应的配额选项 {'2': [3, 4]}
        JSONObject quotaGidOidDict = new JSONObject();
        quotaConditionVos.forEach(quotaCondition -> {
            SurveyQuotaCheckItemVo qcItem = CollectionUtils.isEmpty(quotaCondition.getQuotaCheckitemList())
                    ? new SurveyQuotaCheckItemVo() : quotaCondition.getQuotaCheckitemList().get(0);
            List<SurveyQuotaCheckItemOptionList> optionList = qcItem.getOptionList();
            List<String> ogidList = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(optionList)) {
                ogidList = optionList.stream().map(SurveyQuotaCheckItemOptionList::getOgid).map(String::valueOf)
                        .collect(Collectors.toList());
            }
            JSONObject qcItemDict = new JSONObject();
            String qgid = CollectionUtils.isNotEmpty(quotaCondition.getQgidList()) ? String
                    .valueOf(quotaCondition.getQgidList().get(0).getQgid()) : "";
            if (StringUtils.isNotEmpty(qgid)) {
                qcItemDict.put(qgid, ogidList);
            }
            List<String> oldOidList = quotaGidOidDict.isEmpty() ? null : quotaGidOidDict.getJSONArray(qgid).stream().map(String::valueOf)
                    .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(oldOidList)) {
                oldOidList.addAll(ogidList);
                // {'2': ['3', '4', '3']}
                qcItemDict.put(qgid, oldOidList);
            }
            quotaGidOidDict.putAll(qcItemDict);
        });

        List<String> ret = new ArrayList<>();
        JSONObject qgidOgidsDict = getQuestionGidOgidList(projectId);
        quotaConditionVos.forEach(quotaCondition -> {
            SurveyQuotaCheckItemVo qcItem = CollectionUtils.isEmpty(quotaCondition.getQuotaCheckitemList())
                    ? new SurveyQuotaCheckItemVo() : quotaCondition.getQuotaCheckitemList().get(0);
            List<SurveyQuotaCheckItemOptionList> optionList = qcItem.getOptionList();
            List<String> ogidList = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(optionList)) {
                ogidList = optionList.stream().map(SurveyQuotaCheckItemOptionList::getOgid).map(String::valueOf)
                        .collect(Collectors.toList());
            }

            for (String qgid : ogidList) {
                // 每个问题配额选项
                List<String> options = new ArrayList<>();
                if (quotaGidOidDict.get(qgid) != null) {
                    List<String> collect = quotaGidOidDict.getJSONArray(qgid).stream().map(String::valueOf)
                            .collect(Collectors.toList());
                    options.addAll(collect);
                }
                List<String> ogids = new ArrayList<>();
                if (qgidOgidsDict.get(qgid) != null) {
                    List<String> collect = qgidOgidsDict.getJSONArray(qgid).stream().map(String::valueOf)
                            .collect(Collectors.toList());
                    ogids.addAll(collect);
                }
                options.addAll(ogids);
                Set<String> reSet = new HashSet<>();
                options.forEach(ogid -> reSet.add(qgid + "_" + ogid));
                ret.addAll(reSet);
            }
        });
        JSONObject retJs = new JSONObject();
        retJs.put("option_gids", ret);
        return retJs;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public JSONObject createQuota(String projectId, String quotaId, Object param) {
        // 创建配额条件
        Project project = qdesGrpcClient.getProject(projectId);
        if (project == null) {
            throw new BusinessException("project not found");
        }
        Quota quota = qdesGrpcClient.getQuota(quotaId);
        if (quota == null) {
            throw new BusinessException("quota not found");
        }
        List<String> quotaCondition;
        if (quota.getQuotaType().getNumber() == QUOTA_QUOTA_TYPE_CROSS.getNumber()) {
            quotaCondition = createCrossQuotaCondition(projectId, quotaId, JSONObject.parseObject(JSONObject.toJSONString(param)));
        } else {
            JSONArray qList = JSONArray.parseArray(JSONArray.toJSONString(param));
            quotaCondition = createQuotaCondition(projectId, quotaId, quota, qList);
        }
        JSONObject reJs = new JSONObject();
        reJs.put("quota_cond_id_list", quotaCondition);
        // quota_utils.call_project_notification(project) 该方法配额检查点相关更新通知缓存刷新，实际里面返回值没用到
        // 刷新进度缓存放到create方法内，因为导入配额也调用了create方法
        refreshQuotaProgress(quotaId);
        return reJs;
    }

    @Override
    public JSONObject updateQuota(String projectId, String quotaId, String oid, JSONObject param) {
        // 更新配额条件
        Project project = qdesGrpcClient.getProject(projectId);
        if (project == null) {
            throw new BusinessException("Project not found");
        }
        if (StringUtils.isNotEmpty(oid)) {
            SurveyQuotaCondition quotaCondition = baseMapper.selectById(oid);
            if (quotaCondition == null) {
                throw new BusinessException("QuotaCondition not found");
            }
            if (param == null || param.isEmpty()) {
                return new JSONObject();
            }
            quotaCondition.setProjectId(projectId);
            quotaCondition.setQuotaId(quotaId);
            if (StringUtils.isNotEmpty(param.getString("name"))) {
                quotaCondition.setName(param.getString("name"));
            }
            if (StringUtils.isNotEmpty(param.getString("checkpoints"))) {
                quotaCondition.setCheckpoints(param.getString("checkpoints"));
            }
            quotaCondition.setUpdatedDt(LocalDateTime.now());
            baseMapper.updateById(quotaCondition);
            JSONObject ret = new JSONObject();
            ret.put("id", oid);
            // await quota_utils.call_project_notification(project) 无需实现，python中需要刷新缓存
            refreshQuotaProgress(quotaId);
            return ret;
        }
        Quota quota = qdesGrpcClient.getQuota(quotaId);
        if (quota == null) {
            throw new BusinessException("Quota not found");
        }
        if (QUOTA_QUOTA_TYPE_CROSS.getNumber() == quota.getQuotaTypeValue()) {
            // 设置交叉配额配额检查点
            List<String> quotaCondList = quota.getQuotaCondListList();
            if (CollectionUtils.isEmpty(quotaCondList)) {
                throw new BusinessException("QuotaCondition not found");
            }
            if (param == null || param.isEmpty()) {
                return new JSONObject();
            } else {
                oid = quotaCondList.get(0);
                SurveyQuotaCondition quotaCondition = baseMapper.selectById(oid);
                if (quotaCondition == null) {
                    throw new BusinessException("QuotaCondition not found");
                }
                quotaCondition.setProjectId(projectId);
                quotaCondition.setQuotaId(quotaId);
                if (StringUtils.isNotEmpty(param.getString("name"))) {
                    quotaCondition.setName(param.getString("name"));
                }
                if (StringUtils.isNotEmpty(param.getString("checkpoints"))) {
                    quotaCondition.setCheckpoints(param.getJSONArray("checkpoints").toJSONString());
                }
                quotaCondition.setUpdatedDt(LocalDateTime.now());
                baseMapper.updateById(quotaCondition);
                JSONObject ret = new JSONObject();
                ret.put("id", oid);
                // await quota_utils.call_project_notification(project) 无需实现，python中需要刷新缓存
                refreshQuotaProgress(quotaId);
                return ret;
            }
        }
        if (param.get("idList") == null) {
            throw new BusinessException("idList is required");
        }
        List<String> idList = param.getJSONArray("idList").stream().map(String::valueOf)
                .collect(Collectors.toList());
        SurveyQuotaCondition condition = new SurveyQuotaCondition();
        condition.setQuotaId(quotaId);
        condition.setProjectId(projectId);
        condition.setCheckpoints(param.getJSONArray("checkpoints").toJSONString());
        condition.setUpdatedDt(LocalDateTime.now());
        LambdaQueryWrapper<SurveyQuotaCondition> cWrapper = new LambdaQueryWrapper<>();
        cWrapper.in(SurveyQuotaCondition::getId, idList);
        int modifiedCount = baseMapper.update(condition, cWrapper);
        if (modifiedCount > 0) {
            // await quota_utils.call_project_notification(project) 无需实现，python中需要刷新缓存
            refreshQuotaProgress(quotaId);
        }
        JSONObject ret = new JSONObject();
        ret.put("modified_count", modifiedCount);
        return ret;
    }

    @Override
    public JSONObject updateNormalQuota(String projectId, String quotaId, JSONObject param) {
        log.info("Now to updateNormalQuota, projectId = {}, quotaId = {}, param = {}", projectId, quotaId, param);
        Project project = qdesGrpcClient.getProject(projectId);
        if (project == null) {
            throw new BusinessException("Project is not found");
        }
        if (param.get("numbers") == null) {
            throw new BusinessException("numbers is not found");
        }
        List<Integer> numbers = param.getJSONArray("numbers").toJavaList(Integer.class);
        // 注意，这个地方的顺序需要与获取配额列表的顺序保持一致，
        // 配额列表接口/api/survey/projects/([a-fA-F0-9]{24})/quotas/([a-fA-F0-9]{24})/quotaconditions/([a-fA-F0-9]{24})?
        List<SurveyQuotaCondition> quotaConditionList = baseMapper.selectList(new LambdaQueryWrapper<SurveyQuotaCondition>()
                .eq(SurveyQuotaCondition::getQuotaId, quotaId).orderByAsc(SurveyQuotaCondition::getCreatedDt));
        if (CollectionUtils.isEmpty(quotaConditionList)) {
            throw new BusinessException("SurveyQuotaCondition is not found");
        }
        List<String> quotacheckitemIdList = new ArrayList<>();
        Map<Integer, SurveyQuotaCondition> zipQuotas = handle(numbers, quotaConditionList);
        for (Integer number : zipQuotas.keySet()) {
            SurveyQuotaCondition obj = zipQuotas.get(number);
            SurveyQuotaCheckItem quotaCheckItem = surveyQuotaCheckItemMapper.selectOne(new LambdaQueryWrapper<SurveyQuotaCheckItem>()
                    .eq(SurveyQuotaCheckItem::getQuotaCondId, obj.getId()).last("limit 1"));
            quotaCheckItem.setNum(number);
            surveyQuotaCheckItemMapper.updateById(quotaCheckItem);
            quotacheckitemIdList.add(quotaCheckItem.getId());
        }
        JSONObject re = new JSONObject();
        re.put("quotacheckitem_id_list", quotacheckitemIdList);
        refreshQuotaProgress(quotaId);
        return re;
    }

    @Override
    public JSONObject updateQuotaItem(String projectId, String quotaId, String quotaCondId, String id, JSONObject param) {
        // 更新配额检查项:普通配额一个QuotaCondition对应一个QuotaCheckItem;
        //        普通配额更新配额数据，普通配额批量设置配额数量
        //        交叉配额一个QuotaCondition对应笛卡尔积个QuotaCheckItem
        Project project = qdesGrpcClient.getProject(projectId);
        if (project == null) {
            throw new BusinessException("Project not found");
        }

        if (StringUtils.isNotEmpty(id)) {
            SurveyQuotaCheckItem checkItem = surveyQuotaCheckItemMapper.selectById(id);
            if (checkItem == null) {
                throw new BusinessException("QuotaCheckItem not found");
            }

            if (param != null && !param.isEmpty()) {
                SurveyQuotaCheckItem quotaCheckItem = new SurveyQuotaCheckItem();
                if (param.containsKey("quota_cond_id")) {
                    quotaCheckItem.setQuotaCondId(param.getString("quota_cond_id"));
                }
                if (param.containsKey("project_id")) {
                    quotaCheckItem.setProjectId(param.getString("project_id"));
                }
                if (param.containsKey("num")) {
                    quotaCheckItem.setNum(param.getInteger("num"));
                }
                quotaCheckItem.setId(id);
                quotaCheckItem.setUpdatedDt(LocalDateTime.now());
                surveyQuotaCheckItemMapper.updateById(quotaCheckItem);
                // call_project_notification(project) 刷新缓存，无需开发
                refreshQuotaProgress(quotaId);
                JSONObject ret = new JSONObject();
                ret.put("id", id);
                return ret;
            } else {
                return new JSONObject();
            }
        }

        if (!param.containsKey("numbers")) {
            throw new BusinessException("numbers is require");
        }
        JSONArray numberArray = param.getJSONArray("numbers");
        List<String> numbers = numberArray.stream().map(a -> a.toString().replace("\n", "")
                .replace(";", "")).collect(Collectors.toList());
        LambdaQueryWrapper<SurveyQuotaCheckItem> itemWrapper = Wrappers.lambdaQuery();
        itemWrapper.eq(SurveyQuotaCheckItem::getQuotaCondId, quotaCondId);
        List<SurveyQuotaCheckItem> quotaCheckItems = surveyQuotaCheckItemMapper.selectList(itemWrapper);
        if (CollectionUtils.isEmpty(quotaCheckItems)) {
            throw new BusinessException("QuotaCheckItem not found");
        }
        if (quotaCheckItems.size() < numbers.size()) {
            throw new BusinessException("配额数量个数不正确");
        }
        for (String n : numbers) {
            if (!NumberUtils.isDigits(n)) {
                throw new BusinessException("配额数量必须是自然数");
            }
        }
        List<String> quotaCheckItemIdList = new ArrayList<>();
        for (int i = 0; i < numbers.size(); i++) {
            int number = Integer.parseInt(numbers.get(i));
            SurveyQuotaCheckItem item = quotaCheckItems.get(i);
            item.setNum(number);
            surveyQuotaCheckItemMapper.updateById(item);
            quotaCheckItemIdList.add(item.getId());
        }
        // call_project_notification(project) 刷新缓存，无需开发
        refreshQuotaProgress(quotaId);
        JSONObject ret = new JSONObject();
        ret.put("quotacheckitem_id_list", quotaCheckItemIdList);
        return ret;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public JSONObject deleteQuota(String projectId, String quotaId, String oid, JSONObject param) {
        Project project = qdesGrpcClient.getProject(projectId);
        if (project == null) {
            throw new BusinessException("Project not found");
        }
        if (StringUtils.isNotEmpty(oid)) {
            SurveyQuotaCondition quotaCondition = baseMapper.selectById(oid);
            if (quotaCondition == null) {
                throw new BusinessException("QuotaCondition not found");
            }
            int deleteCount = baseMapper.deleteById(oid);

            // 删除QDES_QUOTA表中"quota_cond_list"字段中的对应id
            deleteQdesQuotaCondition(oid, quotaId);
            deleteManyQuotaCheckItem(oid, quotaId);
            // await quota_utils.call_project_notification(project) 刷新缓存，无需实现
            refreshQuotaProgress(quotaId);
            JSONObject ret = new JSONObject();
            ret.put("deletedCount", deleteCount);
            return ret;
        }

        JSONArray idListArray = param.getJSONArray("idList");
        if (idListArray == null || idListArray.isEmpty()) {
            throw new BusinessException("idList is required");
        }
        List<String> idList = idListArray.toJavaList(String.class);
        int deleteCount;
        for (String oneId : idList) {
            // 删除QDES_QUOTA表中"quota_cond_list"字段中的对应id
            deleteQdesQuotaCondition(oneId, quotaId);
            deleteManyQuotaCheckItem(oneId, quotaId);
        }
        deleteCount = baseMapper.deleteBatchIds(idList);
        if (deleteCount > 0) {
            // await quota_utils.call_project_notification(project) 刷新缓存，无需实现
            refreshQuotaProgress(quotaId);
        }
        JSONObject ret = new JSONObject();
        ret.put("deletedCount", deleteCount);
        return ret;
    }

    private void deleteQdesQuotaCondition(String quotaCondId, String quotaId) {
        // 删除QDES_QUOTA表中"quota_cond_list"字段中的对应id
        Quota quota = qdesGrpcClient.getQuota(quotaId);
        if (quota == null) {
            return;
        }
        List<String> quotaCondList = quota.getQuotaCondListList();
        if (quotaCondList.contains(quotaCondId)) {
            quotaCondList = quotaCondList.stream().filter(q -> !q.equals(quotaCondId)).collect(Collectors.toList());
            Quota.Builder builder = Quota.newBuilder();
            builder.addAllQuotaCondList(quotaCondList);
            qdesGrpcClient.updateQuota(quotaId, builder.build());
        }
    }

    private void deleteManyQuotaCheckItem(String oid, String quotaId) {
//        LambdaQueryWrapper<SurveyQuotaConditionQgidList> defaultWrapper = Wrappers.lambdaQuery();
//        defaultWrapper.eq(SurveyQuotaConditionQgidList::getQuotaCondId, oid);
//        List<SurveyQuotaConditionQgidList> quotaConditionQgidLists = surveyQuotaConditionQgidListMapper.selectList(defaultWrapper);

        LambdaQueryWrapper<SurveyQuotaCheckItem> itemWrapper = Wrappers.lambdaQuery();
        itemWrapper.eq(SurveyQuotaCheckItem::getQuotaCondId, oid);
        List<SurveyQuotaCheckItem> quotaCheckItems = surveyQuotaCheckItemMapper.selectList(itemWrapper);
        if (CollectionUtils.isEmpty(quotaCheckItems)) {
            return;
        }
        List<String> idList = quotaCheckItems.stream().map(SurveyQuotaCheckItem::getId).collect(Collectors.toList());

        LambdaQueryWrapper<SurveyQuotaCheckItemOptionList> itemOptionWrapper = Wrappers.lambdaQuery();
        itemOptionWrapper.in(SurveyQuotaCheckItemOptionList::getQuotaCheckitemId, idList);
        surveyQuotaCheckItemOptionListMapper.delete(itemOptionWrapper);

        LambdaQueryWrapper<SurveyQuotaCheckItemOptionCombies> combiesWrapper = Wrappers.lambdaQuery();
        combiesWrapper.in(SurveyQuotaCheckItemOptionCombies::getQuotaCheckitemId, idList);
        surveyQuotaCheckItemOptionCombiesMapper.delete(combiesWrapper);

        surveyQuotaCheckItemMapper.deleteBatchIds(idList);

        // 移除respondent表quota_checkitem_list，mysql方式需要删除RespondentQuotaCheckitem表中记录
        LambdaQueryWrapper<SurveyRespondentQuotaCheckItem> resWrapper = Wrappers.lambdaQuery();
        resWrapper.in(SurveyRespondentQuotaCheckItem::getQuotaCheckitemId, idList);
        surveyRespondentQuotaCheckItemMapper.delete(resWrapper);
    }

    @Override
    public List<String> createCrossQuotaCondition(String projectId, String quotaId, JSONObject param) {
        // 新增交叉配额条件
        List<String> quotaCondIdList = dikaQuBuild(projectId, quotaId, param);
        refreshQuotaProgress(quotaId);
        return quotaCondIdList;
    }

    private List<String> dikaQuBuild(String projectId, String quotaId, JSONObject self) {
        // 生成笛卡尔积
        //        为了处理组合的情况，使用2次笛卡尔积
        //        没有组合：
        //            final_list：[['1_1'], ['2_1'], ['3_1'], ['4_1']]
        //            option_combie_list：[('1_1', '2_1', '3_1', '4_1')]
        //            strlist: ['1_1|2_1|3_1|4_1']
        //        组合：
        //            final_list：[['1_1'], ['2_1'], ['3_1'], ['4_2', '4_3']]
        //            option_combie_list：[('1_1', '2_1', '3_1', '4_2'), ('1_1', '2_1', '3_1', '4_3')]
        //            strlist: ['1_1|2_1|3_1|4_2', '1_1|2_1|3_1|4_3']
        if (self.get("operate") != null) {
            Quota.Builder builder = Quota.newBuilder();
            builder.setAvaQgidList(self.getString("ava_qgid_list"));
            qdesGrpcClient.updateQuota(quotaId, builder.build());
            return new ArrayList<>();
        }
        SurveyDikaVo surveyDikaVo = genDikaXy(self);
        List<List<List<String>>> dataList = surveyDikaVo.getDataList();
        dataList = dataList.stream().filter(CollectionUtils::isNotEmpty).collect(Collectors.toList());
        List<Integer> qgidList = surveyDikaVo.getQgidList();
        qgidList = qgidList.stream().filter(Objects::nonNull).collect(Collectors.toList());
        Quota.Builder quotaPrams = surveyDikaVo.getQuotaPrams();
        if (CollectionUtils.isEmpty(qgidList)) {
            cleanCrossQuota(quotaId, self.getString("ava_qgid_list"));
            return new ArrayList<>();
        }
        SurveyQuotaConditionVo quotaCondition = findAndCreateQuotaCondition(projectId, quotaId, self, qgidList);
        String quotaCondId = quotaCondition.getId();
        deleteManyWuotaCheckItemDika(quotaCondition);
        List<String> quotaCheckitemList = new ArrayList<>();
        List<List<List<String>>> lists = Lists.cartesianProduct(dataList);
        for (int i = 0; i < lists.size(); i++) {
            List<List<String>> finalList = lists.get(i);
            List<List<String>> optionCombieList = Lists.cartesianProduct(finalList);
            List<String> strList = new ArrayList<>();
            for (List<String> oclist : optionCombieList) {
                oclist = oclist.stream().sorted().collect(Collectors.toList());
                String ocstr = String.join("|", oclist);
                strList.add(ocstr);
            }
            strList = strList.stream().sorted().collect(Collectors.toList());
            Integer num = null;
            if (self.getJSONArray("checkitem_nums") != null && !self.getJSONArray("checkitem_nums").isEmpty()) {
                num = i < self.getJSONArray("checkitem_nums").size() ? self.getJSONArray("checkitem_nums")
                        .getInteger(i) : null;
            }
            SurveyQuotaCheckItem quotacheckitem = findAndCreateQuotacheckitem(projectId, quotaCondId, strList, num);
            quotaCheckitemList.add(quotacheckitem.getId());
        }
        log.info("cross quota create quota_cond_id:{} quota_checkitem_list:{}", quotaCondId, quotaCheckitemList);
        // update_quotacondition 该方法已不用
        quotaPrams.addAllQuotaCondList(Collections.singletonList(quotaCondId));
        qdesGrpcClient.updateQuota(quotaId, quotaPrams.build());
        genQuotaNumDelay(projectId, quotaId, null);
        return Collections.singletonList(quotaCondId);
    }

    private SurveyQuotaCheckItem findAndCreateQuotacheckitem(String projectId, String quotaCondId, List<String> strList, Integer num) {
        List<String> optionList = new ArrayList<>();
        for (String optionCombie : strList) {
            String[] qidOidList = optionCombie.split("\\|");
            for (String qidOid : qidOidList) {
                String oid = qidOid.split("_")[1];
                if (!optionList.contains(oid)) {
                    optionList.add(oid);
                }
            }
        }
        // 创建QuotaCheckItem
        SurveyQuotaCheckItem checkItem = new SurveyQuotaCheckItem();
        checkItem.setQuotaCondId(quotaCondId);
        checkItem.setProjectId(projectId);
        if (num != null) {
            checkItem.setNum(num);
        }
        // 设置默认值
        checkItem.setCurCount(0);
        checkItem.setOptionCombies("[]");
        surveyQuotaCheckItemMapper.insert(checkItem);
        // 创建QuotaCheckItemOptionList
        List<SurveyQuotaCheckItemOptionList> quotackiOptions = new ArrayList<>();
        for (String option : optionList) {
            SurveyQuotaCheckItemOptionList itemOptionList = new SurveyQuotaCheckItemOptionList();
            itemOptionList.setQuotaCheckitemId(checkItem.getId());
            itemOptionList.setOgid(Integer.valueOf(option));
            quotackiOptions.add(itemOptionList);
        }
        if (CollectionUtils.isNotEmpty(quotackiOptions)) {
            surveyQuotaCheckItemOptionListMapper.insertBatchSomeColumn(quotackiOptions);
        }
        // 创建QuotaCheckItemOptionCombies
        List<SurveyQuotaCheckItemOptionCombies> quotackiOptionCombies = new ArrayList<>();
        for (String optionCombine : strList) {
            SurveyQuotaCheckItemOptionCombies optionCombies = new SurveyQuotaCheckItemOptionCombies();
            optionCombies.setQuotaCheckitemId(checkItem.getId());
            optionCombies.setOptionCombine(optionCombine);
            quotackiOptionCombies.add(optionCombies);
        }
        if (CollectionUtils.isNotEmpty(quotackiOptionCombies)) {
            surveyQuotaCheckItemOptionCombiesMapper.insertBatchSomeColumn(quotackiOptionCombies);
        }
        return checkItem;
    }

    private void deleteManyWuotaCheckItemDika(SurveyQuotaConditionVo quotaCondition) {
        // 删除nin quota_checkitem_list 的quotacheckitem
        List<SurveyQuotaCheckItemVo> quotaCheckitemList = quotaCondition.getQuotaCheckitemList();
        if (CollectionUtils.isEmpty(quotaCheckitemList)) {
            return;
        }
        delQuotaCheckitemRelation(quotaCondition.getId());
        List<String> idList = quotaCheckitemList.stream().map(SurveyQuotaCheckItem::getId).collect(Collectors.toList());
        // 移除respondent表quota_checkitem_list，mysql方式需要删除RespondentQuotaCheckitem表中记录
        LambdaQueryWrapper<SurveyRespondentQuotaCheckItem> itemWrapper = Wrappers.lambdaQuery();
        itemWrapper.in(SurveyRespondentQuotaCheckItem::getQuotaCheckitemId, idList);
        surveyRespondentQuotaCheckItemMapper.delete(itemWrapper);
    }

    private void delQuotaCheckitemRelation(String quotaCondId) {
        // 删除quotacheckitem需要联动删除QuotaCheckItemOptionList， QuotaCheckItemOptionCombies
        //    该方法未通用方法，3个表一起删除
        LambdaQueryWrapper<SurveyQuotaCheckItem> itemWrapper = Wrappers.lambdaQuery();
        itemWrapper.eq(SurveyQuotaCheckItem::getQuotaCondId, quotaCondId);
        List<SurveyQuotaCheckItem> quotaCheckItems = surveyQuotaCheckItemMapper.selectList(itemWrapper);
        if (CollectionUtils.isEmpty(quotaCheckItems)) {
            return;
        }
        List<String> quotaCkIds = quotaCheckItems.stream().map(SurveyQuotaCheckItem::getId).collect(Collectors.toList());
        LambdaQueryWrapper<SurveyQuotaCheckItemOptionList> itemOptionWrapper = Wrappers.lambdaQuery();
        itemOptionWrapper.in(SurveyQuotaCheckItemOptionList::getQuotaCheckitemId, quotaCkIds);
        surveyQuotaCheckItemOptionListMapper.delete(itemOptionWrapper);

        LambdaQueryWrapper<SurveyQuotaCheckItemOptionCombies> combiesWrapper = Wrappers.lambdaQuery();
        combiesWrapper.in(SurveyQuotaCheckItemOptionCombies::getQuotaCheckitemId, quotaCkIds);
        surveyQuotaCheckItemOptionCombiesMapper.delete(combiesWrapper);

        surveyQuotaCheckItemMapper.delete(itemWrapper);
    }

    private SurveyQuotaConditionVo findAndCreateQuotaCondition(String projectId, String quotaId, JSONObject param,
                                                               List<Integer> qgidList) {
        SurveyQuotaCondition quotaCondition = new SurveyQuotaCondition();
        quotaCondition.setName("");
        quotaCondition.setProjectId(projectId);
        quotaCondition.setQuotaId(quotaId);
        String checkNode = param.getString("check_node");
        if (checkNode != null) {
            quotaCondition.setCheckpoints(checkNode);
        } else {
            JSONArray checkpoints = genBackCheckpoints(projectId, quotaId, param);
            quotaCondition.setCheckpoints(checkpoints.toJSONString());
        }
        LambdaQueryWrapper<SurveyQuotaCondition> qWrapper = new LambdaQueryWrapper<>();
        qWrapper.eq(SurveyQuotaCondition::getQuotaId, quotaId).last("limit 1");
        SurveyQuotaCondition condition = baseMapper.selectOne(qWrapper);
        if (condition == null) {
            baseMapper.insert(quotaCondition);
            condition = quotaCondition;
        } else {
            condition.setName(quotaCondition.getName());
            condition.setProjectId(quotaCondition.getProjectId());
            condition.setCheckpoints(quotaCondition.getCheckpoints());
            condition.setUpdatedDt(LocalDateTime.now());
            baseMapper.updateById(condition);
        }
        // QuotaConditionQgidList
        for (Integer qgid : qgidList) {
            LambdaQueryWrapper<SurveyQuotaConditionQgidList> qgWrapper = new LambdaQueryWrapper<>();
            qgWrapper.eq(SurveyQuotaConditionQgidList::getQuotaCondId, condition.getId())
                    .eq(SurveyQuotaConditionQgidList::getQgid, qgid).last("limit 1");
            SurveyQuotaConditionQgidList qgidList1 = surveyQuotaConditionQgidListMapper.selectOne(qgWrapper);
            if (qgidList1 == null) {
                qgidList1 = new SurveyQuotaConditionQgidList();
                qgidList1.setQgid(qgid);
                qgidList1.setQuotaCondId(condition.getId());
                surveyQuotaConditionQgidListMapper.insert(qgidList1);
            }
        }
        LambdaQueryWrapper<SurveyQuotaConditionQgidList> qgWrapper = new LambdaQueryWrapper<>();
        qgWrapper.eq(SurveyQuotaConditionQgidList::getQuotaCondId, condition.getId())
                .notIn(SurveyQuotaConditionQgidList::getQgid, qgidList);
        surveyQuotaConditionQgidListMapper.delete(qgWrapper);
        return setSurveyQuotaConditionVo(condition, false);
    }

    private JSONArray genBackCheckpoints(String projectId, String quotaId, JSONObject self) {
        // 交叉配额自动生成配额检查点，生成规则：
        //        col1、col2、row1、row2最后面的题目同配额检查点内的题目比较，
        //        如果都在这些题目后面，则全部替换
        //        如果在这些题目中间，则替换最小的题目
        //        如果在这些题目前面，则不替换
        List<String> qgidList = new ArrayList<>();
        qgidList.add(self.getJSONArray("col1").isEmpty() ? null : self.getJSONArray("col1").getJSONObject(0).getString("qgid"));
        qgidList.add(self.getJSONArray("col2").isEmpty() ? null : self.getJSONArray("col2").getJSONObject(0).getString("qgid"));
        qgidList.add(self.getJSONArray("row1").isEmpty() ? null : self.getJSONArray("row1").getJSONObject(0).getString("qgid"));
        qgidList.add(self.getJSONArray("row2").isEmpty() ? null : self.getJSONArray("row2").getJSONObject(0).getString("qgid"));
        Project project = qdesGrpcClient.getProject(projectId);
        QStruct qStruct = qdesGrpcClient.getQStruct(project.getStructId());
        List<String> projQgidList = new ArrayList<>();
        List<JSONObject> projQlist = new ArrayList<>();
        for (JSONObject q : walkQstructQuestions(JSONObject.parseObject(qStruct.getData()))) {
            projQgidList.add(q.getString("gid"));
            projQlist.add(q);
        }
        List<JSONObject> behindQgidList = new ArrayList<>();
        for (String qgid : qgidList) {
            if (StringUtils.isNotEmpty(qgid)) {
                int idx = projQgidList.indexOf(qgid);
                JSONObject onQgid = new JSONObject();
                onQgid.put("qgid", qgid);
                onQgid.put("idx", idx);
                onQgid.put("title", projQlist.get(idx).getString("title"));
                onQgid.put("cid", projQlist.get(idx).get("cid"));
                behindQgidList.add(onQgid);
            }
        }
        behindQgidList = behindQgidList.stream().sorted(Comparator.comparing(o -> (Integer) o
                .getOrDefault("idx", 0))).collect(Collectors.toList());
        Integer idx = CollectionUtils.isEmpty(behindQgidList) ? null : behindQgidList.get(behindQgidList.size() - 1).getInteger("idx");
        LambdaQueryWrapper<SurveyQuotaCondition> conWrapper = new LambdaQueryWrapper<>();
        conWrapper.eq(SurveyQuotaCondition::getQuotaId, quotaId);
        List<SurveyQuotaCondition> quotaConditions = baseMapper.selectList(conWrapper);
        JSONObject behindQgidDict = CollectionUtils.isEmpty(behindQgidList) ? new JSONObject() : behindQgidList.get(behindQgidList.size() - 1);
        if (CollectionUtils.isEmpty(quotaConditions) || StringUtils.isEmpty(quotaConditions.get(0).getCheckpoints())) {
            // 如果未设置过配额检查点，直接设置最后面的题目
            JSONObject oneNode = new JSONObject();
            oneNode.put("gid", behindQgidDict.get("qgid"));
            oneNode.put("title", behindQgidDict.get("title"));
            oneNode.put("cid", behindQgidDict.get("cid"));
            JSONArray checkNode = new JSONArray();
            checkNode.add(oneNode);
            return checkNode;
        } else {
            // 如果已设置过配额检查点，更新前面的题目
            String points = quotaConditions.get(0).getCheckpoints();
            List<JSONObject> needRemovePoints = new ArrayList<>();
            JSONArray checkpoints = JSONArray.parseArray(points);
            for (int i = 0; i < checkpoints.size(); i++) {
                JSONObject point = checkpoints.getJSONObject(i);
                int gid = projQgidList.indexOf(point.getString("gid"));
                if (idx != null && idx > gid) {
                    needRemovePoints.add(point);
                }
            }
            for (JSONObject nrp : needRemovePoints) {
                checkpoints.remove(nrp);
            }
            if (CollectionUtils.isNotEmpty(needRemovePoints)) {
                JSONObject oneNode = new JSONObject();
                oneNode.put("gid", behindQgidDict.get("qgid"));
                oneNode.put("title", behindQgidDict.get("title"));
                oneNode.put("cid", behindQgidDict.get("cid"));
                checkpoints.add(oneNode);
            }
            return checkpoints;
        }
    }

    private void cleanCrossQuota(String quotaId, String avaQgidList) {
        // 假如清空了数据，则执行下列model
        LambdaQueryWrapper<SurveyQuotaCondition> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SurveyQuotaCondition::getQuotaId, quotaId);
        List<SurveyQuotaCondition> quotaConditions = baseMapper.selectList(wrapper);
        if (CollectionUtils.isNotEmpty(quotaConditions)) {
            List<String> conditionIds = quotaConditions.stream().map(SurveyQuotaCondition::getId).collect(Collectors.toList());
            LambdaQueryWrapper<SurveyQuotaConditionQgidList> qgidWrapper = new LambdaQueryWrapper<>();
            qgidWrapper.in(SurveyQuotaConditionQgidList::getQuotaCondId, conditionIds);
            surveyQuotaConditionQgidListMapper.delete(qgidWrapper);
            LambdaQueryWrapper<SurveyQuotaCheckItem> checkWrapper = new LambdaQueryWrapper<>();
            checkWrapper.in(SurveyQuotaCheckItem::getQuotaCondId, conditionIds);
            List<SurveyQuotaCheckItem> quotaCheckItems = surveyQuotaCheckItemMapper.selectList(checkWrapper);
            if (CollectionUtils.isNotEmpty(quotaCheckItems)) {
                List<String> qckids = quotaCheckItems.stream().map(SurveyQuotaCheckItem::getId).collect(Collectors.toList());
                // 删除QuotaCheckItem关联表
                LambdaQueryWrapper<SurveyQuotaCheckItemOptionList> optionWrapper = new LambdaQueryWrapper<>();
                optionWrapper.in(SurveyQuotaCheckItemOptionList::getQuotaCheckitemId, qckids);
                surveyQuotaCheckItemOptionListMapper.delete(optionWrapper);
                LambdaQueryWrapper<SurveyQuotaCheckItemOptionCombies> combiesWrapper = new LambdaQueryWrapper<>();
                combiesWrapper.in(SurveyQuotaCheckItemOptionCombies::getQuotaCheckitemId, qckids);
                surveyQuotaCheckItemOptionCombiesMapper.delete(combiesWrapper);
                surveyQuotaCheckItemMapper.deleteBatchIds(qckids);
            }
        }
        Quota.Builder builder = Quota.newBuilder();
        builder.setCol1("[]");
        builder.setCol2("[]");
        builder.setRow1("[]");
        builder.setRow2("[]");
        if (StringUtils.isNotEmpty(avaQgidList)) {
            // 防止前端不传ava_qgid_list，不更新ava_qgid_list，保存原来值
            builder.setAvaQgidList(avaQgidList);
        }
        qdesGrpcClient.updateQuota(quotaId, builder.build());
    }

    private SurveyDikaVo genDikaXy(JSONObject self) {
        SurveyDikaVo surveyDikaVo = new SurveyDikaVo();
        List<List<List<String>>> dataList = new ArrayList<>();
        dataList.add(genQgidOgidList(self.getJSONArray("row1")));
        dataList.add(genQgidOgidList(self.getJSONArray("row2")));
        dataList.add(genQgidOgidList(self.getJSONArray("col1")));
        dataList.add(genQgidOgidList(self.getJSONArray("col2")));
        // 用于更新quota
        Quota.Builder quotaParams = Quota.newBuilder();
        quotaParams.setCol1(self.getString("col1"));
        quotaParams.setCol2(self.getString("col2"));
        quotaParams.setRow1(self.getString("row1"));
        quotaParams.setRow2(self.getString("row2"));
        if (self.get("ava_qgid_list") != null) {
            // 防止前端不传ava_qgid_list，不更新ava_qgid_list，保存原来值
            quotaParams.setAvaQgidList(self.getString("ava_qgid_list"));
        }
        // 用来更新quotaconditon
        List<Integer> qgidList = new ArrayList<>();
        qgidList.add(self.getJSONArray("col1").isEmpty() ? null : self.getJSONArray("col1").getJSONObject(0).getInteger("qgid"));
        qgidList.add(self.getJSONArray("col2").isEmpty() ? null : self.getJSONArray("col2").getJSONObject(0).getInteger("qgid"));
        qgidList.add(self.getJSONArray("row1").isEmpty() ? null : self.getJSONArray("row1").getJSONObject(0).getInteger("qgid"));
        qgidList.add(self.getJSONArray("row2").isEmpty() ? null : self.getJSONArray("row2").getJSONObject(0).getInteger("qgid"));
        surveyDikaVo.setQuotaPrams(quotaParams);
        surveyDikaVo.setDataList(dataList);
        surveyDikaVo.setQgidList(qgidList);
        return surveyDikaVo;
    }

    private List<List<String>> genQgidOgidList(JSONArray ogidList) {
        // 生成一级列、二级列、一级行、二级行的 qgid_ogid
        List<List<String>> qOgidList = new ArrayList<>();
        if (ogidList == null || ogidList.isEmpty()) {
            return new ArrayList<>();
        }
        List<JSONObject> items = getXyItemsByCrossOne(ogidList);
        for (JSONObject ogidMap : items) {
            String qgid = ogidMap.getString("qgid");
            Object ogid = ogidMap.get("ogid");
            List<String> sublist = new ArrayList<>();
            if (ogid instanceof List) {
                List<String> list = Convert.toList(String.class, ogid);
                for (String gid : list) {
                    if (gid.contains("_")) {
                        sublist.add(qgid + "_" + gid.split("_")[1]);
                    } else {
                        sublist.add(qgid + "_" + gid);
                    }
                }
            } else {
                String ogidStr = ogid.toString();
                if (ogidStr.contains("_")) {
                    sublist.add(qgid + "_" + ogidStr.split("_")[1]);
                } else {
                    sublist.add(qgid + "_" + ogidStr);
                }
            }
            qOgidList.add(sublist);
        }
        return qOgidList;
    }

    private List<JSONObject> getXyItemsByCrossOne(JSONArray crossOne) {
        // 解析前端给到的交叉项结构
        //    :param cross_one:
        //    [
        //        {
        //            "qgid":1,
        //            "ogid": [[1,2],3],
        //            "otitle":"组1",
        //        },{
        //            "qgid": 1,
        //            "ogid": 4,
        //            "otitle": "选项4",
        //        }
        //    ]
        //    :return:
        //    [{
        //            'qgid': 1,
        //            'ogid': [1,2,3]
        //            "otitle":"组1",
        //        }, {
        //            'qgid': 1,
        //            'ogid': '4',
        //            'otitle': 'd',
        //        }
        //    ]
        List<JSONObject> ret = new ArrayList<>();
        for (int i = 0; i < crossOne.size(); i++) {
            JSONObject item = crossOne.getJSONObject(i);
            if (item.get("ogid") instanceof List) {
                List<Object> tiledOgids = new ArrayList<>();
                getOpitonList(item.get("ogid"), tiledOgids);
                item.put("ogid", tiledOgids);
            }
            ret.add(item);
        }
        return ret;
    }

    private void getOpitonList(Object ogid, List<Object> tiledOgids) {
        if (ogid instanceof List) {
            List<?> list = Convert.toList(ogid);
            for (Object item : list) {
                getOpitonList(item, tiledOgids);
            }
        } else {
            tiledOgids.add(ogid);
        }
    }

    private List<String> createQuotaCondition(String projectId, String quotaId, Quota quota, JSONArray qList) {
        List<String> quotaCondIdList = new ArrayList<>();
        for (int i = 0; i < qList.size(); i++) {
            JSONObject qmap = qList.getJSONObject(i);
            String name = qmap.getString("title");
            Integer qGid = qmap.getInteger("q_gid");
            Integer oGid = qmap.getInteger("o_gid");
            Integer num = qmap.getInteger("num");
            String checkNode = qmap.getString("check_node");

            SurveyQuotaCondition quotaCondition = new SurveyQuotaCondition();
            quotaCondition.setQuotaId(quotaId);
            quotaCondition.setProjectId(projectId);
            quotaCondition.setName(name);
            quotaCondition.setCheckpoints(checkNode);
            baseMapper.insert(quotaCondition);

            if (qGid != null) {
                SurveyQuotaConditionQgidList qgidList = new SurveyQuotaConditionQgidList();
                qgidList.setQgid(qGid);
                qgidList.setQuotaCondId(quotaCondition.getId());
                surveyQuotaConditionQgidListMapper.insert(qgidList);
            }

            SurveyQuotaCheckItem checkItem = new SurveyQuotaCheckItem();
            checkItem.setQuotaCondId(quotaCondition.getId());
            checkItem.setProjectId(projectId);
            if (num != null) {
                checkItem.setNum(num);
            }
            // 设置默认值
            checkItem.setCurCount(0);
            checkItem.setOptionCombies("[]");
            surveyQuotaCheckItemMapper.insert(checkItem);

            SurveyQuotaCheckItemOptionCombies optionCombies = new SurveyQuotaCheckItemOptionCombies();
            optionCombies.setQuotaCheckitemId(checkItem.getId());
            optionCombies.setOptionCombine(qGid + "_" + oGid);
            surveyQuotaCheckItemOptionCombiesMapper.insert(optionCombies);
            if (oGid != null) {
                SurveyQuotaCheckItemOptionList optionList = new SurveyQuotaCheckItemOptionList();
                optionList.setQuotaCheckitemId(checkItem.getId());
                optionList.setOgid(oGid);
                surveyQuotaCheckItemOptionListMapper.insert(optionList);
            }
            quotaCondIdList.add(quotaCondition.getId());
        }
        // stat
        List<String> quotaCondList = new ArrayList<>(quota.getQuotaCondListList());
        if (CollectionUtils.isEmpty(quotaCondList)) {
            quotaCondList = new ArrayList<>();
        }
        for (String quotaCondId : quotaCondIdList) {
            if (!quotaCondList.contains(quotaCondId)) {
                quotaCondList.add(quotaCondId);
            }
        }
        Quota.Builder builder = Quota.newBuilder();
        builder.addAllQuotaCondList(quotaCondList);
        qdesGrpcClient.updateQuota(quotaId, builder.build());
        // end
        genQuotaNumDelay(projectId, quotaId, null);
        refreshQuotaProgress(quotaId);
        return quotaCondIdList;
    }

    @Override
    public void refreshQuotaProgress(String quotaId) {
        genNormalQuotaDataCommon(quotaId);
        genCrossQuotaDataCommon(quotaId);
    }

    @Override
    public void genQuotaNumDelay(String projectId, String quotaId, Long seq) {
        // 生成配额数量
        if (seq == null) {
            redisService.set("gen_quota_num_delay#" + projectId + "#" + quotaId, 1);
        } else {
            redisService.set("gen_quota_num_resume_delay#" + projectId + "#" + quotaId + "#" + seq, 1);
        }
        // ==============business start
        LambdaQueryWrapper<SurveyRespondent> defaultWrapper = Wrappers.lambdaQuery();
        defaultWrapper.eq(SurveyRespondent::getProjectId, projectId)
                .eq(SurveyRespondent::getStatus, RESPONDENT_STATUS_FINISH.getNumber())
                .eq(SurveyRespondent::getDataStatus, RESPONDENT_DATA_STATUS_FORMAL.getNumber());
        if (seq != null) {
            defaultWrapper.eq(SurveyRespondent::getSeq, seq);
        }
        List<SurveyRespondent> res = surveyRespondentMapper.selectList(defaultWrapper);
        log.info("[1]genQuotaNum projectId={}, quotaId={}", projectId, quotaId);
        // 可以一次全部取出，但是占内存，改成每次取patch_num个样本的配额，保存到数据库后重置rspd_quotacheckitems，减少内存支出
        List<SurveyRespondentQuotaCheckItem> rspdQuotacheckitems = new ArrayList<>();
        List<String> rids = new ArrayList<>();
        List<String> quotaCheckitemIds = new ArrayList<>();
        int patchNum = 10000;
        int i = 0;
        for (SurveyRespondent rspd : res) {
            Long seq1 = rspd.getSeq();
            LambdaQueryWrapper<SurveyRespondentData> dataWrapper = Wrappers.lambdaQuery();
            dataWrapper.eq(SurveyRespondentData::getProjectId, projectId)
                    .eq(SurveyRespondentData::getSeq, seq1).last("limit 1");
            SurveyRespondentData respondentData = surveyRespondentDataMapper.selectOne(dataWrapper);
            if (respondentData == null) {
                continue;
            }
            String pageAnswers = respondentData.getAnswers();
            List<SurveyQuotaCheckItemVo> quotaCheckitemList = getAllMatchQuotaCheckitemV2(projectId, pageAnswers, quotaId);
            if (CollectionUtils.isNotEmpty(quotaCheckitemList)) {
                for (SurveyQuotaCheckItemVo qckItem : quotaCheckitemList) {
                    SurveyRespondentQuotaCheckItem qckJs = new SurveyRespondentQuotaCheckItem();
                    qckJs.setRid(rspd.getId());
                    qckJs.setQuotaCheckitemId(qckItem.getId());
                    rspdQuotacheckitems.add(qckJs);
                    quotaCheckitemIds.add(qckItem.getId());
                }
            }
            rids.add(rspd.getId());
            i += 1;
            if (i >= patchNum) {
                // 满patch_num执行保存配额，并重置计数
                updateQuotaCountPatch(projectId, quotaId, rspdQuotacheckitems, rids, quotaCheckitemIds);
                log.info("[2]gen_quota_num project_id={}, quota_id={}", projectId, quotaId);
                rspdQuotacheckitems = new ArrayList<>();
                rids = new ArrayList<>();
                quotaCheckitemIds = new ArrayList<>();
                i = 0;
            }
        }
        // 样本量%patch_num，剩余的样本计数
        log.info("[3]gen_quota_num project_id={}, quota_id={}", projectId, quotaId);
        if (CollectionUtils.isNotEmpty(rspdQuotacheckitems)) {
            updateQuotaCountPatch(projectId, quotaId, rspdQuotacheckitems, rids, quotaCheckitemIds);
        }
        // ==============business end
        if (seq != null) {
            redisService.del("gen_quota_num_delay#" + projectId + "#" + quotaId);
        } else {
            redisService.del("gen_quota_num_resume_delay#" + projectId + "#" + quotaId + "#" + seq);
        }
    }

    private void updateQuotaCountPatch(String projectId, String quotaId, List<SurveyRespondentQuotaCheckItem>
            rspdQuotacheckitems, List<String> rids, List<String> quotaCheckitemIds) {
        refreshQuotaProgress(quotaId);
        if (CollectionUtils.isNotEmpty(rids)) {
            LambdaQueryWrapper<SurveyRespondentQuotaCheckItem> wrapper = new LambdaQueryWrapper<>();
            surveyRespondentQuotaCheckItemMapper.delete(wrapper);
        }
        if (CollectionUtils.isNotEmpty(rspdQuotacheckitems)) {
            surveyRespondentQuotaCheckItemMapper.insertBatchSomeColumn(rspdQuotacheckitems);
        }
        // # 发布新增配额消息
        //    params = {
        //        "project_id": project_id,
        //        "quota_checkitem_ids": quota_checkitem_ids
        //    }
        //    await redis_mq.publish(topics.survey_add_quote, params)
    }

    @Override
    public List<JSONObject> genNormalQuotaDataCommon(String quotaId) {
        LambdaQueryWrapper<SurveyQuotaCondition> defaultWrapper = Wrappers.lambdaQuery();
        defaultWrapper.eq(SurveyQuotaCondition::getQuotaId, quotaId);
        List<SurveyQuotaCondition> qcList = baseMapper.selectList(defaultWrapper);
        if (CollectionUtils.isEmpty(qcList)) {
            return new ArrayList<>();
        }
        List<String> condIdList = new ArrayList<>();
        JSONObject condNameDict = new JSONObject();
        for (SurveyQuotaCondition qc : qcList) {
            condIdList.add(qc.getId());
            SurveyQuotaConditionVo conditionVo = setSurveyQuotaConditionVo(qc, false);
            if (CollectionUtils.isNotEmpty(conditionVo.getQuotaCheckitemList())) {
                condNameDict.put(conditionVo.getQuotaCheckitemList().get(0).getId(), qc.getName());
            }
        }

        LambdaQueryWrapper<SurveyQuotaCheckItem> itemWrapper = Wrappers.lambdaQuery();
        itemWrapper.in(SurveyQuotaCheckItem::getQuotaCondId, condIdList);
        List<SurveyQuotaCheckItem> qciList = surveyQuotaCheckItemMapper.selectList(itemWrapper);
        List<String> checkitemIdList = new ArrayList<>();
        qciList.forEach(qci -> checkitemIdList.add(qci.getId()));
        // inner join 和 group by
        if (CollectionUtils.isEmpty(checkitemIdList)) {
            return new ArrayList<>();
        }
        List<SurveyCheckItemRespVo> rs = surveyRespondentQuotaCheckItemMapper.getItemResp(checkitemIdList);
        JSONObject numDict = new JSONObject();
        for (SurveyCheckItemRespVo group : rs) {
            numDict.put(group.getQuotaCheckitemId(), group.getCount());
        }
        List<JSONObject> trlist = new ArrayList<>();
        for (SurveyQuotaCheckItem qci : qciList) {
            int defineNum = qci.getNum() != null ? qci.getNum() : 0;
            int num = (int) numDict.getOrDefault(qci.getId(), 0);
            JSONObject tr = new JSONObject();
            tr.put("title", condNameDict.getString(qci.getId()));
            tr.put("define_num", qci.getNum());
            tr.put("num", num);
            tr.put("qc_id", qci.getId());
            tr.put("count", defineNum);
            tr.put("remain", defineNum - num);
            trlist.add(tr);
        }
        return trlist;
    }

    @Override
    public JSONObject genCrossQuotaDataCommon(String quotaId) {
        LambdaQueryWrapper<SurveyQuotaCondition> defaultWrapper = Wrappers.lambdaQuery();
        defaultWrapper.eq(SurveyQuotaCondition::getQuotaId, quotaId);
        List<SurveyQuotaCondition> qcList = baseMapper.selectList(defaultWrapper);

        List<Integer> defineNumList = new ArrayList<>();
        List<Integer> numList = new ArrayList<>();
        List<JSONObject> ccQuotaList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(qcList)) {
            // 交叉配额只有一个quotacondition
            // 一个quotaconditon对应多个quotacheckitem
            LambdaQueryWrapper<SurveyQuotaCheckItem> itemWrapper = Wrappers.lambdaQuery();
            itemWrapper.in(SurveyQuotaCheckItem::getQuotaCondId, qcList.get(0).getId());
            List<SurveyQuotaCheckItem> qciList = surveyQuotaCheckItemMapper.selectList(itemWrapper);
            if (CollectionUtils.isEmpty(qciList)) {
                return new JSONObject();
            }
            List<String> checkitemIdList;
            defineNumList = qciList.stream().map(SurveyQuotaCheckItem::getNum).collect(Collectors.toList());
            checkitemIdList = qciList.stream().map(SurveyQuotaCheckItem::getId).collect(Collectors.toList());
            // inner join 和 group by
            List<SurveyCheckItemRespVo> rs = surveyRespondentQuotaCheckItemMapper.getItemResp(checkitemIdList);
            // 保证顺序同checkitem_id_list一致
            JSONObject numDict = new JSONObject();
            for (SurveyCheckItemRespVo group : rs) {
                numDict.put(group.getQuotaCheckitemId(), group.getCount());
            }
            for (SurveyQuotaCheckItem qci : qciList) {
                int defineNum = qci.getNum() != null ? qci.getNum() : 0;
                int num = (int) numDict.getOrDefault(qci.getId(), 0);
                JSONObject tr = new JSONObject();
                tr.put("cc_id", qci.getId());
                tr.put("count", defineNum);
                tr.put("remain", defineNum - num);
                ccQuotaList.add(tr);
                numList.add(num);
            }
        }
        JSONObject ret = new JSONObject();
        ret.put("define_num_list", defineNumList);
        ret.put("num_list", numList);
        ret.put("ccquota_list", ccQuotaList);
        return ret;
    }

    @Override
    public JSONObject getQuotaCross(String projectId, String quotaId) {
        Quota quota = qdesGrpcClient.getQuota(quotaId);
        if (quota == null) {
            throw new BusinessException("Quota not found");
        }
        JSONObject ret = new JSONObject();
        ret.put("col1", new ArrayList<>());
        ret.put("col2", new ArrayList<>());
        ret.put("row1", new ArrayList<>());
        ret.put("row2", new ArrayList<>());
        ret.put("quota_checkitem_list", new ArrayList<>());
        if (CollectionUtils.isEmpty(quota.getQuotaCondListList())) {
            return ret;
        }
        String quotaCondId = quota.getQuotaCondList(0);
        SurveyQuotaCondition quotaCondition = baseMapper.selectById(quotaCondId);
        if (quotaCondition == null) {
            return ret;
        }
        LambdaQueryWrapper<SurveyQuotaCheckItem> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SurveyQuotaCheckItem::getQuotaCondId, quotaCondId);
        List<SurveyQuotaCheckItem> quotaCheckItems = surveyQuotaCheckItemMapper.selectList(wrapper);
        if (CollectionUtils.isNotEmpty(quotaCheckItems)) {
            List<JSONObject> jsonObjects = new ArrayList<>();
            quotaCheckItems.forEach(quotaCheckItem -> {
                JSONObject oneJs = new JSONObject();
                oneJs.put("id", quotaCheckItem.getId());
                oneJs.put("num", quotaCheckItem.getNum() == null ? "" : quotaCheckItem.getNum());
                jsonObjects.add(oneJs);
            });
            ret.put("quota_checkitem_list", jsonObjects);
        }
        ret.put("col1", StringUtils.isEmpty(quota.getCol1()) ? new ArrayList<>() : JSONArray.parseArray(quota.getCol1()));
        ret.put("col2", StringUtils.isEmpty(quota.getCol2()) ? new ArrayList<>() : JSONArray.parseArray(quota.getCol2()));
        ret.put("row1", StringUtils.isEmpty(quota.getRow1()) ? new ArrayList<>() : JSONArray.parseArray(quota.getRow1()));
        ret.put("row2", StringUtils.isEmpty(quota.getRow2()) ? new ArrayList<>() : JSONArray.parseArray(quota.getRow2()));
        return ret;
    }

    @Override
    public JSONObject updateQuotaCrossItem(String projectId, String quotaId, String id, JSONObject param) {
        // 更新配额检查项:普通配额一个QuotaCondition对应一个QuotaCheckItem;
        //        交叉配额一个QuotaCondition对应笛卡尔积个QuotaCheckItem
        // 创建配额条件
        Project project = qdesGrpcClient.getProject(projectId);
        if (project == null) {
            throw new BusinessException("project not found");
        }
        Quota quota = qdesGrpcClient.getQuota(quotaId);
        if (quota == null) {
            throw new BusinessException("quota not found");
        }
        if (CollectionUtils.isEmpty(quota.getQuotaCondListList())) {
            throw new BusinessException("QuotaCondition not found");
        }
        JSONObject ret = new JSONObject();
        String quotaCondId = quota.getQuotaCondList(0);
        if (StringUtils.isNotEmpty(id)) {
            if (param != null && !param.isEmpty()) {
                SurveyQuotaCheckItem quotaCheckItem = surveyQuotaCheckItemMapper.selectById(id);
                if (quotaCheckItem == null) {
                    throw new BusinessException("QuotaCheckItem not found");
                }
                quotaCheckItem.setProjectId(projectId);
                quotaCheckItem.setQuotaCondId(quotaCondId);
                if (param.containsKey("num")) {
                    quotaCheckItem.setNum(param.getInteger("num"));
                }
                if (param.containsKey("cur_count")) {
                    quotaCheckItem.setCurCount(param.getInteger("cur_count"));
                }
                if (param.containsKey("option_combies")) {
                    quotaCheckItem.setOptionCombies(param.getString("option_combies"));
                }
                surveyQuotaCheckItemMapper.alwaysUpdateSomeColumnById(quotaCheckItem);
                // await quota_utils.call_project_notification(project) 无需实现，python中需要刷新缓存
                refreshQuotaProgress(quotaId);
                ret.put("id", id);
            }
            return ret;
        }

        if (!param.containsKey("numbers")) {
            throw new BusinessException("numbers is require");
        }
        String numberStr = param.getString("numbers");
        numberStr.replace("，", ",");
        List<String> numbers = Arrays.asList(numberStr.split(","));
        numbers = numbers.stream().map(a -> a.trim().replace("\n", "")
                .replace(";", "")).collect(Collectors.toList());

        LambdaQueryWrapper<SurveyQuotaCheckItem> itemWrapper = Wrappers.lambdaQuery();
        itemWrapper.eq(SurveyQuotaCheckItem::getQuotaCondId, quotaCondId);
        List<SurveyQuotaCheckItem> quotaCheckItems = surveyQuotaCheckItemMapper.selectList(itemWrapper);
        if (CollectionUtils.isEmpty(quotaCheckItems)) {
            throw new BusinessException("QuotaCheckItem not found");
        }
        if (quotaCheckItems.size() != numbers.size()) {
            throw new BusinessException("请按配额表结构，输入全部配额的数量");
        }
        for (String n : numbers) {
            if (!NumberUtils.isDigits(n)) {
                throw new BusinessException("配额数量必须是自然数");
            }
        }
        List<String> quotaCheckItemIdList = new ArrayList<>();
        for (int i = 0; i < numbers.size(); i++) {
            int number = Integer.parseInt(numbers.get(i));
            SurveyQuotaCheckItem item = quotaCheckItems.get(i);
            item.setNum(number);
            surveyQuotaCheckItemMapper.updateById(item);
            quotaCheckItemIdList.add(item.getId());
        }
        // call_project_notification(project) 刷新缓存，无需开发
        refreshQuotaProgress(quotaId);
        ret.put("quotacheckitem_id_list", quotaCheckItemIdList);
        return ret;
    }

    @Override
    public List<SurveyQuotaCheckItemVo> getAllMatchQuotaCheckitemV2(String projectId, String pageAnswers, String quotaId) {
        log.info("getAllMatchQuotaCheckitemV2 projectId={},pageAnswers={},quotaId={}", projectId, pageAnswers, quotaId);
        // 根据样本答案，获取指定配额的匹配到的quota_checkitem；
        // quota_id为None时，获取满足该答案所有配额的quota_checktitem;
        List<String> quotaCondIds = new ArrayList<>();
        List<SurveyQuotaCondition> surveyQuotaConditions;
        LambdaQueryWrapper<SurveyQuotaCondition> defaultWrapper = Wrappers.lambdaQuery();
        defaultWrapper.eq(SurveyQuotaCondition::getProjectId, projectId);
        if (StringUtils.isNotEmpty(quotaId)) {
            defaultWrapper.eq(SurveyQuotaCondition::getQuotaId, quotaId);
            surveyQuotaConditions = baseMapper.selectList(defaultWrapper);
            if (CollectionUtils.isNotEmpty(surveyQuotaConditions)) {
                quotaCondIds = surveyQuotaConditions.stream().map(SurveyQuotaCondition::getId).collect(Collectors.toList());
            }
        } else {
            surveyQuotaConditions = baseMapper.selectList(defaultWrapper);
        }
        List<List<String>> qgidLists = new ArrayList<>();
        surveyQuotaConditions.forEach(qc -> {
            SurveyQuotaConditionVo conditionVo = setSurveyQuotaConditionVo(qc, false);
            qgidLists.add(conditionVo.getQgidList().stream().map(SurveyQuotaConditionQgidList::getQgid)
                    .map(String::valueOf).collect(Collectors.toList()));
        });
        List<String> quotaCheckValList = getQuotaCheckValList(qgidLists, pageAnswers);
        List<SurveyQuotaCheckItemVo> allCheckItems = new ArrayList<>();
        if (CollectionUtils.isEmpty(quotaCheckValList)) {
            return allCheckItems;
        }
        return surveyQuotaCheckItemMapper.getItemByParam(projectId, quotaCheckValList, quotaCondIds);
    }

    private List<String> getQuotaCheckValList(List<List<String>> qgidLists, String pageAnswers) {
        // 根据配额项的题目gids组合(普通配额只有1个gid)，从答案中生成选项组合
        List<String> quotaCheckValList = new ArrayList<>();
        if (CollectionUtils.isEmpty(qgidLists) || StringUtils.isEmpty(pageAnswers)) {
            return quotaCheckValList;
        }
        List<String> optionGidList;
        for (List<String> qgidList : qgidLists) {
            List<List<String>> unionGidLists = new ArrayList<>();
            // 对于交叉配额，qgid_list是交叉的题目gid数组，union_gid_list生成 [["Q1_1","Q1_2"],["Q2_1","Q2_2"]]
            //        对应普通配额，qgid_list是只有1题的数组，union_gid_list:[["Q1_1","Q1_2"]]
            for (String qgid : qgidList) {
                JSONObject pageAnswerJs = JSONObject.parseObject(pageAnswers);
                if (pageAnswerJs.containsKey(qgid)) {
                    Object answer = pageAnswerJs.get(qgid);
                    if (answer instanceof List) {
                        Object an1 = ((List<?>) answer).get(0);
                        if (an1 instanceof List) {
                            // 多选题
                            List<String> answers = new ArrayList<>();
                            for (Object subAnswer : (List<?>) answer) {
                                answers.addAll(Convert.toList(String.class, subAnswer));
                            }
                            optionGidList = answers;
                        } else {
                            optionGidList = new ArrayList<>(Collections.singletonList(String.valueOf(an1)));
                        }
                        Collections.sort(optionGidList);
                        unionGidLists.add(optionGidList.stream().map(ogid -> qgid + "_" + ogid)
                                .collect(Collectors.toList()));
                    }
                }
            }

            if (CollectionUtils.isNotEmpty(unionGidLists)) {
                List<String> checkValueList = new ArrayList<>();
                List<List<String>> optionCombieList = Lists.cartesianProduct(unionGidLists);
                List<List<String>> newoptionCombieList = new ArrayList<>(optionCombieList);
                for (List<String> ocList : newoptionCombieList) {
                    List<String> newocList = new ArrayList<>(ocList);
                    Collections.sort(newocList);
                    String ocstr = Joiner.on("|").join(newocList);
                    checkValueList.add(ocstr);
                }
                checkValueList = checkValueList.stream().distinct().collect(Collectors.toList());
                Collections.sort(checkValueList);
                quotaCheckValList.addAll(checkValueList);
                quotaCheckValList = quotaCheckValList.stream().distinct().collect(Collectors.toList());
            }
        }
        return quotaCheckValList;
    }

    private JSONObject getQuestionGidOgidList(String projectId) {
        // qgid_dict = dict([(str(q.get("gid")), q) for q in questions])
        List<JSONObject> questionCidGidDict = getQuestionCidGidDict(projectId);
        JSONObject ret = new JSONObject();
        if (CollectionUtils.isNotEmpty(questionCidGidDict)) {
            for (JSONObject question : questionCidGidDict) {
                String qgid = question.getString("gid");
                JSONArray items = question.getJSONArray("items");
                if (items == null || items.isEmpty()) {
                    continue;
                }
                List<JSONObject> options = new ArrayList<>();
                for (int i = 0; i < items.size(); i++) {
                    JSONObject item = items.getJSONObject(i);
                    if ("O".equals(item.getString("type"))) {
                        options.add(item);
                    }
                }
                if (CollectionUtils.isEmpty(options)) {
                    continue;
                }
                ret.put(qgid, options.stream().map(j -> j.getString("gid")).collect(Collectors.toList()));
            }
        }
        return ret;
    }

    private List<JSONObject> getQuestionCidGidDict(String projectId) {
        // 根据project_id获取题目字段，key: gid, value: question
        Project project = qdesGrpcClient.getProject(projectId);
        if (project == null) {
            return new ArrayList<>();
        }
        // 根据project获取题目信息
        QStruct qStruct = qdesGrpcClient.getQStruct(project.getStructId());
        JSONObject data = qStruct == null ? new JSONObject() : JSONObject.parseObject(qStruct.getData());
        // 选项关联，导出项目，去掉被关联过来的选项
        return new ArrayList<>(walkQstructQuestions(data));
    }

    public List<JSONObject> walkQstructQuestions(JSONObject data) {
        List<JSONObject> questionList = new ArrayList<>();
        if (data == null) {
            return questionList;
        }
        String itemsStr = data.getString("items");
        if (org.apache.commons.lang3.StringUtils.isEmpty(itemsStr)) {
            return questionList;
        }
        List<String> items = JSONArray.parseArray(itemsStr, String.class);
        for (String item : items) {
            if (NumberUtils.isDigits(item)) {
                continue;
            }
            JSONObject itemJs = JSONObject.parseObject(item);
            String type = itemJs.getString("type");
            if ("Q".equals(type)) {
                if (itemJs.getString("items") != null) {
                    itemJs.put("items", JSONArray.parseArray(itemJs.getString("items")));
                }
                questionList.add(itemJs);
            } else if ("G".equals(type)) {
                questionList.addAll(walkQstructQuestions(itemJs));
            }
        }
        return questionList;
    }

    /**
     * 批量删除配额
     *
     * @param QuotaIds ids
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteQuotas(List<String> QuotaIds) {
        //删除子表及子子表
        List<SurveyQuotaCondition> conditionList = baseMapper.selectList(new LambdaQueryWrapper<SurveyQuotaCondition>().in(SurveyQuotaCondition::getQuotaId, QuotaIds));
        if (CollUtil.isNotEmpty(conditionList)) {
            List<String> quotaCondIds = conditionList.stream().map(SurveyQuotaCondition::getId).collect(Collectors.toList());

            //condition表的子表有2个，item表和qgid list表
            int delete = surveyQuotaConditionGidListMapper.delete(new LambdaQueryWrapper<SurveyQuotaConditionQgidList>().in(SurveyQuotaConditionQgidList::getQuotaCondId, quotaCondIds));
            log.info("删除surveyQuotaConditionGidList结果：{}", delete);

            //先删除关联的条件表和item表，然后再删除配额主表
            //注意这里python条件表和item表是rpc删除的，这里直接删除
            //item相关表有3张，optioncombies,optionlist,respondentquatacheckitem
            LambdaQueryWrapper<SurveyQuotaCheckItem> checkItemWrapper = new LambdaQueryWrapper<SurveyQuotaCheckItem>().in(SurveyQuotaCheckItem::getQuotaCondId, quotaCondIds);
            List<SurveyQuotaCheckItem> itemList = surveyQuotaCheckItemMapper.selectList(checkItemWrapper);
            if (CollUtil.isNotEmpty(itemList)) {
                List<String> itemIds = itemList.stream().map(SurveyQuotaCheckItem::getId).collect(Collectors.toList());

                int delete1 = surveyQuotaCheckItemOptionCombiesMapper.delete(new LambdaQueryWrapper<SurveyQuotaCheckItemOptionCombies>().in(SurveyQuotaCheckItemOptionCombies::getQuotaCheckitemId, itemIds));
                log.info("删除surveyQuotaCheckItemCombies结果：{}", delete1);

                int delete2 = surveyQuotaCheckItemOptionListMapper.delete(new LambdaQueryWrapper<SurveyQuotaCheckItemOptionList>().in(SurveyQuotaCheckItemOptionList::getQuotaCheckitemId, itemIds));
                log.info("删除surveyQuotaCheckItemOptionlist结果：{}", delete2);

                int delete3 = surveyRespondentQuotaCheckItemMapper.delete(new LambdaQueryWrapper<SurveyRespondentQuotaCheckItem>().in(SurveyRespondentQuotaCheckItem::getQuotaCheckitemId, itemIds));
                log.info("删除surveyRespondentquotacheckitem结果：{}", delete3);
            }
            int delete1 = surveyQuotaCheckItemMapper.delete(checkItemWrapper);
            log.info("删除surveyQuotaCheckItem结果：{}", delete1);
            baseMapper.deleteBatchIds(conditionList);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void copyQuota(List<String> conditionList, String oldId, String newId) {
        if (!CollectionUtils.isEmpty(conditionList)) {
            conditionList.forEach(conditionId -> {
                SurveyQuotaCondition oldCondition = baseMapper.selectById(conditionId);
                if (oldCondition != null) {
                    // 插入condition
                    SurveyQuotaCondition condition = new SurveyQuotaCondition();
                    condition.setName(oldCondition.getName());
                    condition.setProjectId(newId);
                    condition.setQuotaId(oldCondition.getQuotaId());
                    condition.setCheckpoints(oldCondition.getCheckpoints());
                    baseMapper.insert(condition);
                    LambdaQueryWrapper<SurveyQuotaConditionQgidList> qdesQuotaWrapper = Wrappers.lambdaQuery();
                    qdesQuotaWrapper.eq(SurveyQuotaConditionQgidList::getQuotaCondId, oldId);
                    List<SurveyQuotaConditionQgidList> quotaQgidLists = surveyQuotaConditionGidListMapper
                            .selectList(qdesQuotaWrapper);
                    if (!CollectionUtils.isEmpty(quotaQgidLists)) {
                        // 插入quotaQgidLists
                        List<SurveyQuotaConditionQgidList> newQuataList = new ArrayList<>();
                        quotaQgidLists.forEach(gidList -> {
                            SurveyQuotaConditionQgidList newGid = new SurveyQuotaConditionQgidList();
                            newGid.setQuotaCondId(condition.getId());
                            newGid.setQgid(gidList.getQgid());
                            newQuataList.add(newGid);
                        });
                        if (!CollectionUtils.isEmpty(newQuataList)) {
                            surveyQuotaConditionGidListMapper.insertBatchSomeColumn(newQuataList);
                        }
                    }
                    // 插入checkItems
                    LambdaQueryWrapper<SurveyQuotaCheckItem> qdesItemWrapper = Wrappers.lambdaQuery();
                    qdesItemWrapper.eq(SurveyQuotaCheckItem::getQuotaCondId, oldId);
                    List<SurveyQuotaCheckItem> checkItems = surveyQuotaCheckItemMapper.selectList(qdesItemWrapper);
                    if (!CollectionUtils.isEmpty(checkItems)) {
                        checkItems.forEach(checkItem -> {
                            SurveyQuotaCheckItem newItem = new SurveyQuotaCheckItem();
                            newItem.setQuotaCondId(condition.getId());
                            newItem.setProjectId(newId);
                            newItem.setNum(checkItem.getNum());
                            // 设置默认值
                            newItem.setCurCount(0);
                            newItem.setOptionCombies("[]");
                            surveyQuotaCheckItemMapper.insert(newItem);
                            // 插入对应的option_list
                            LambdaQueryWrapper<SurveyQuotaCheckItemOptionList> optionWrapper = Wrappers.lambdaQuery();
                            optionWrapper.eq(SurveyQuotaCheckItemOptionList::getQuotaCheckitemId, checkItem.getId());
                            List<SurveyQuotaCheckItemOptionList> itemOptions = surveyQuotaCheckItemOptionListMapper.selectList(optionWrapper);
                            if (!CollectionUtils.isEmpty(itemOptions)) {
                                List<SurveyQuotaCheckItemOptionList> newItemOptions = new ArrayList<>();
                                itemOptions.forEach(itemOption -> {
                                    SurveyQuotaCheckItemOptionList newItemOption = new SurveyQuotaCheckItemOptionList();
                                    newItemOption.setOgid(itemOption.getOgid());
                                    newItemOption.setQuotaCheckitemId(newItem.getId());
                                    newItemOptions.add(newItemOption);
                                });
                                if (!CollectionUtils.isEmpty(newItemOptions)) {
                                    surveyQuotaCheckItemOptionListMapper.insertBatchSomeColumn(newItemOptions);
                                }
                            }
                            // 插入对应的list_quotacheckitemoptioncombies
                            LambdaQueryWrapper<SurveyQuotaCheckItemOptionCombies> combiesWrapper = Wrappers.lambdaQuery();
                            combiesWrapper.eq(SurveyQuotaCheckItemOptionCombies::getQuotaCheckitemId, checkItem.getId());
                            List<SurveyQuotaCheckItemOptionCombies> itemCombies = surveyQuotaCheckItemOptionCombiesMapper.selectList(combiesWrapper);
                            if (!CollectionUtils.isEmpty(itemCombies)) {
                                List<SurveyQuotaCheckItemOptionCombies> newItemCombies = new ArrayList<>();
                                itemCombies.forEach(itemCom -> {
                                    SurveyQuotaCheckItemOptionCombies newItemCom = new SurveyQuotaCheckItemOptionCombies();
                                    newItemCom.setOptionCombine(itemCom.getOptionCombine());
                                    newItemCom.setQuotaCheckitemId(newItem.getId());
                                    newItemCombies.add(newItemCom);
                                });
                                if (!CollectionUtils.isEmpty(newItemCombies)) {
                                    surveyQuotaCheckItemOptionCombiesMapper.insertBatchSomeColumn(newItemCombies);
                                }
                            }
                        });
                    }
                }
            });

        }
    }

    @Override
    public void createImportQuota(List<String> conditionList, String targetProjectId, String newQuotaId, JSONObject data) {
        if (data == null) {
            return;
        }
        JSONObject qgidMap = data.getJSONObject("qgid_map");
        JSONObject ogidMap = data.getJSONObject("ogid_map");
        if (!CollectionUtils.isEmpty(conditionList)) {
            conditionList.forEach(conditionId -> {
                SurveyQuotaCondVo quotaCond = baseMapper.getQuotacondition(conditionId);
                if (quotaCond != null) {
                    // 插入condition
                    SurveyQuotaCondition newCond = new SurveyQuotaCondition();
                    newCond.setName(quotaCond.getName());
                    newCond.setProjectId(targetProjectId);
                    newCond.setQuotaId(newQuotaId);
                    newCond.setCheckpoints(regenCheckpoints(quotaCond.getQgidList(), quotaCond
                            .getCheckpoints(), data, qgidMap));
                    baseMapper.insert(newCond);
                    List<String> quotaQgidLists = regeQgidList(quotaCond.getQgidList(), qgidMap);
                    if (!CollectionUtils.isEmpty(quotaQgidLists)) {
                        // 插入quotaQgidLists
                        List<SurveyQuotaConditionQgidList> newQuataList = new ArrayList<>();
                        quotaQgidLists.forEach(qgid -> {
                            SurveyQuotaConditionQgidList newGid = new SurveyQuotaConditionQgidList();
                            newGid.setQuotaCondId(newCond.getId());
                            newGid.setQgid(Integer.parseInt(qgid));
                            newQuataList.add(newGid);
                        });
                        if (!CollectionUtils.isEmpty(newQuataList)) {
                            surveyQuotaConditionGidListMapper.insertBatchSomeColumn(newQuataList);
                        }
                    }
                    // 插入checkItems
                    for (SurveyQuotaItemVo itemItem : quotaCond.getQuotaCheckitemList()) {
                        SurveyQuotaCheckItem quotaCheckitem = surveyQuotaCheckItemMapper.selectById(itemItem.getId());
                        if (quotaCheckitem == null) {
                            continue;
                        }
                        SurveyQuotaCheckItem newItem = new SurveyQuotaCheckItem();
                        newItem.setQuotaCondId(newCond.getId());
                        newItem.setProjectId(targetProjectId);
                        newItem.setNum(quotaCheckitem.getNum());
                        newItem.setOptionCombies(regenOptionCombies(quotaCheckitem.getOptionCombies(), qgidMap, ogidMap));
                        // 设置默认值
                        newItem.setCurCount(0);
                        newItem.setOptionCombies("[]");
                        surveyQuotaCheckItemMapper.insert(newItem);
                        // 插入对应的option_list
                        LambdaQueryWrapper<SurveyQuotaCheckItemOptionList> optionWrapper = Wrappers.lambdaQuery();
                        optionWrapper.eq(SurveyQuotaCheckItemOptionList::getQuotaCheckitemId, quotaCheckitem.getId());
                        List<SurveyQuotaCheckItemOptionList> itemOptions = surveyQuotaCheckItemOptionListMapper.selectList(optionWrapper);
                        if (!CollectionUtils.isEmpty(itemOptions)) {
                            List<SurveyQuotaCheckItemOptionList> newItemOptions = new ArrayList<>();
                            List<Integer> ogids = regenOptionList(itemOptions.stream().map(SurveyQuotaCheckItemOptionList::getOgid)
                                    .collect(Collectors.toList()), ogidMap);
                            ogids.forEach(ogid -> {
                                SurveyQuotaCheckItemOptionList newItemOption = new SurveyQuotaCheckItemOptionList();
                                newItemOption.setOgid(ogid);
                                newItemOption.setQuotaCheckitemId(newItem.getId());
                                newItemOptions.add(newItemOption);
                            });
                            if (!CollectionUtils.isEmpty(newItemOptions)) {
                                surveyQuotaCheckItemOptionListMapper.insertBatchSomeColumn(newItemOptions);
                            }
                        }
                    }
                    // python中更新配额条件，但是没有走到流程，不需要更新
                }
            });
        }
    }

    @Override
    public JSONObject getQuotaStruct(String projectId) {
        log.info("Now to getQuotaStruct, projectId = {}", projectId);
        // 用于编辑问卷校验配额
        //        [{
        //            'ogid': 100,
        //            'qgid': 1,
        //            'checkGids': [5,6]
        //        },...]
        JSONObject re = new JSONObject();
        JSONObject qstructV2 = getQuotaQstructV2(projectId);
        re.put("option_quota_list", Optional.ofNullable(qstructV2.get("used_options_ret")).orElse(new ArrayList<>()));
        re.put("cross_qidlist", Optional.ofNullable(qstructV2.get("cross_qid_lists")).orElse(new ArrayList<>()));
        re.put("compose_gid_map", getComposeGids(projectId));
        return re;
    }

    private JSONObject getQuotaQstructV2(String projectId) {
        // 生成题目配额结构，用于编辑端校验配额.
        //    思路：
        //    1、根据project_id从quota_checkitem找到用到的ogids；
        //    2、根据ogids找到相关的quota_conidtion;
        //    3、遍历quota_condition，用qgid_list及checkpoints，从已经筛出来的ogids，生成需要的信息
        //    :param project_id:
        //    :return: [{"ogid":ogid, "qgid":qgid, "checkGids":checkGids}]
        List<SurveyQuotaCheckItem> items = surveyQuotaCheckItemMapper.selectList(new LambdaQueryWrapper<SurveyQuotaCheckItem>()
                .eq(SurveyQuotaCheckItem::getProjectId, projectId));
        JSONObject re = new JSONObject();
        if (CollectionUtils.isEmpty(items)) {
            return re;
        }
        // 询结果形如[{'quota_cond_id': '626217e2c39f3900087b5c15', 'ogid_list': [3,4]},{'quota_cond_id': '626217e2c39f3900087b5c1a', 'ogid_list': [3,4]}]
        JSONArray checkItems = new JSONArray();
        List<String> quotaCondIds = new ArrayList<>();
        for (SurveyQuotaCheckItem item : items) {
            List<SurveyQuotaCheckItemOptionList> optionLists = surveyQuotaCheckItemOptionListMapper.selectList(new LambdaQueryWrapper<SurveyQuotaCheckItemOptionList>()
                    .eq(SurveyQuotaCheckItemOptionList::getQuotaCheckitemId, item.getId()));
            JSONObject itemJs = new JSONObject();
            itemJs.put("quota_cond_id", item.getQuotaCondId());
            quotaCondIds.add(item.getQuotaCondId());
            if (CollectionUtils.isEmpty(optionLists)) {
                itemJs.put("ogid_list", new ArrayList<>());
            } else {
                itemJs.put("ogid_list", optionLists.stream().map(SurveyQuotaCheckItemOptionList::getOgid).collect(Collectors.toList()));
            }
            checkItems.add(itemJs);
        }
        // 处理交叉配额在左侧，未使用的题目
        List<SurveyQuotaCondition> quotaCondData = baseMapper.selectList(new LambdaQueryWrapper<SurveyQuotaCondition>()
                .in(SurveyQuotaCondition::getId, quotaCondIds));
        List<Quota> crossQuotas = qdesGrpcClient.getQuotaList(projectId, 1);
        JSONObject qgidDict = getQuestionGidOgidList(projectId);
        // 处理关联表的数据
        JSONObject quotaCondDatas = new JSONObject();
        for (SurveyQuotaCondition quotaCondition : quotaCondData) {
            List<SurveyQuotaConditionQgidList> qgidLists = surveyQuotaConditionQgidListMapper
                    .selectList(new LambdaQueryWrapper<SurveyQuotaConditionQgidList>()
                            .eq(SurveyQuotaConditionQgidList::getQuotaCondId, quotaCondition.getId()));
            JSONObject quoJs = new JSONObject();
            if (CollectionUtils.isEmpty(qgidLists)) {
                quoJs.put("qgid_list", new ArrayList<>());
            } else {
                quoJs.put("qgid_list", qgidLists.stream().map(SurveyQuotaConditionQgidList::getQgid)
                        .collect(Collectors.toList()));
            }
            quoJs.put("checkpoints", JSONArray.parseArray(quotaCondition.getCheckpoints()));
            quotaCondDatas.put(quotaCondition.getId(), quoJs);
        }
        for (int i = 0; i < checkItems.size(); i++) {
            JSONObject item = checkItems.getJSONObject(i);
            item.put("quota_cond_data", quotaCondDatas.get(item.getString("quota_cond_id")));
        }
        // [
        //        {'quota_cond_id': '626217e2c39f3900087b5c15', 'ogid_list': [3],
        //         'quota_cond_data': {'qgid_list': [2],
        //                             'checkpoints': [{'cid': 'Q1', 'gid': '2', 'title': '单选题'}]}},
        //        {'quota_cond_id': '626217e2c39f3900087b5c1a',
        //         'ogid_list': [4],
        //         'quota_cond_data': {'qgid_list': [2],
        //                             'checkpoints': [{'cid': 'Q1', 'gid': '2', 'title': '单选题'}]}}
        //    ]
        List<Integer> crossQidLists = new ArrayList<>();
        JSONObject quotaGidOidDict = new JSONObject();
        for (Quota crossQuota : crossQuotas) {
            JSONArray avaArray = JSONArray.parseArray(crossQuota.getAvaQgidList());
            if (avaArray != null && !avaArray.isEmpty()) {
                for (int i = 0; i < avaArray.size(); i++) {
                    JSONObject avaJs = avaArray.getJSONObject(i);
                    crossQidLists.add(avaJs.getInteger("gid"));
                }
            }
            // 如果有交叉配额，组建一下交叉配额的题目gid以及用到的选项
            // row1 = [{'qgid': '9', 'ogid': '9_3', 'otitle': '选项1'}, {'qgid': '9', 'ogid': '9_7', 'otitle': '3'}]
            // row1_dict = {'9': ['3', '10']}
            if (StringUtils.isNotEmpty(crossQuota.getRow1()) && !"[]".equals(crossQuota.getRow1())) {
                JSONArray rowArray = JSONArray.parseArray(crossQuota.getRow1());
                String qgid = rowArray.getJSONObject(0).getString("qgid");
                List<Integer> newList = new ArrayList<>();
                for (int i = 0; i < rowArray.size(); i++) {
                    JSONObject rowJs = rowArray.getJSONObject(i);
                    newList.add(Integer.parseInt(rowJs.getString("ogid").split("_")[1]));
                    formatOptionDict(quotaGidOidDict, newList, qgid);
                }
            }
            if (StringUtils.isNotEmpty(crossQuota.getRow2()) && !"[]".equals(crossQuota.getRow2())) {
                JSONArray rowArray = JSONArray.parseArray(crossQuota.getRow2());
                String qgid = rowArray.getJSONObject(0).getString("qgid");
                List<Integer> newList = new ArrayList<>();
                for (int i = 0; i < rowArray.size(); i++) {
                    JSONObject rowJs = rowArray.getJSONObject(i);
                    newList.add(Integer.parseInt(rowJs.getString("ogid").split("_")[1]));
                    formatOptionDict(quotaGidOidDict, newList, qgid);
                }
            }
            if (StringUtils.isNotEmpty(crossQuota.getCol1()) && !"[]".equals(crossQuota.getCol1())) {
                JSONArray colArray = JSONArray.parseArray(crossQuota.getCol1());
                String qgid = colArray.getJSONObject(0).getString("qgid");
                List<Integer> newList = new ArrayList<>();
                for (int i = 0; i < colArray.size(); i++) {
                    JSONObject rowJs = colArray.getJSONObject(i);
                    newList.add(Integer.parseInt(rowJs.getString("ogid").split("_")[1]));
                    formatOptionDict(quotaGidOidDict, newList, qgid);
                }
            }
            if (StringUtils.isNotEmpty(crossQuota.getCol2()) && !"[]".equals(crossQuota.getCol2())) {
                JSONArray colArray = JSONArray.parseArray(crossQuota.getCol2());
                String qgid = colArray.getJSONObject(0).getString("qgid");
                List<Integer> newList = new ArrayList<>();
                for (int i = 0; i < colArray.size(); i++) {
                    JSONObject rowJs = colArray.getJSONObject(i);
                    newList.add(Integer.parseInt(rowJs.getString("ogid").split("_")[1]));
                    formatOptionDict(quotaGidOidDict, newList, qgid);
                }
            }
        }

        JSONArray usedOptionsRet = new JSONArray();
        if (checkItems.isEmpty()) {
            re.put("cross_qid_lists", crossQidLists);
            return re;
        }
        for (int i = 0; i < checkItems.size(); i++) {
            JSONObject item = checkItems.getJSONObject(i);
            String qgid = item.getJSONObject("quota_cond_data").getJSONArray("qgid_list").getString(0);
            // 交叉配额已经使用的选项列表
            List<Integer> ogidList = item.getJSONArray("ogid_list").toJavaList(Integer.class);
            List<Integer> oldOidList = Optional.ofNullable(quotaGidOidDict.getJSONArray(qgid)).orElse(new JSONArray())
                    .toJavaList(Integer.class);
            oldOidList.addAll(ogidList);
            // {'2': ['3', '4', '3']}
            quotaGidOidDict.put(qgid, oldOidList);
        }
        for (int i = 0; i < checkItems.size(); i++) {
            JSONObject item = checkItems.getJSONObject(i);
            JSONArray qgidList = item.getJSONObject("quota_cond_data").getJSONArray("qgid_list");
            JSONArray checkpoints = item.getJSONObject("quota_cond_data").getJSONArray("checkpoints");
            List<Integer> checkGids = new ArrayList<>();
            if (checkpoints != null && !checkpoints.isEmpty()) {
                for (int j = 0; j < checkpoints.size(); j++) {
                    JSONObject c = checkpoints.getJSONObject(j);
                    checkGids.add(c.getInteger("gid"));
                }
            }
            if (qgidList != null && !qgidList.isEmpty()) {
                List<String> qgids = qgidList.toJavaList(String.class);
                qgids = qgids.stream().filter(Objects::nonNull).collect(Collectors.toList());
                for (String qgid : qgids) {
                    // 每个问题配额选项
                    List<Integer> options = new ArrayList<>(Optional.ofNullable(quotaGidOidDict.getJSONArray(qgid)).orElse(new JSONArray())
                            .toJavaList(Integer.class));
                    // 题目的选项
                    List<Integer> ogids = Optional.ofNullable(qgidDict.getJSONArray(qgid)).orElse(new JSONArray())
                            .toJavaList(Integer.class);
                    // 已经配置交叉的使用的选项
                    List<Integer> needOgids = new ArrayList<>(CollectionUtils.intersection(ogids, options));
                    for (Integer ogid : needOgids) {
                        JSONObject smap = new JSONObject();
                        smap.put("ogid", ogid);
                        smap.put("qgid", Integer.parseInt(qgid));
                        smap.put("checkGids", checkGids);
                        if (!usedOptionsRet.contains(smap)) {
                            usedOptionsRet.add(smap);
                        }
                    }
                }
            }
        }
        re.put("used_options_ret", usedOptionsRet);
        re.put("cross_qid_lists", crossQidLists);
        return re;
    }

    private JSONObject getComposeGids(String projectId) {
        // 交叉配额设置了组合、不能删除
        //    return:
        //        compose_gid_dict = {
        //            qgid: [ogid]
        //        }
        List<Quota> crossQuotas = qdesGrpcClient.getQuotaList(projectId, QUOTA_QUOTA_TYPE_CROSS.getNumber());
        JSONObject composeGidDict = new JSONObject();
        for (Quota quota : crossQuotas) {
            if (StringUtils.isNotEmpty(quota.getAvaQgidList())) {
                List<JSONObject> avaQgidList = JSONArray.parseArray(quota.getAvaQgidList(), JSONObject.class);
                for (JSONObject qmap : avaQgidList) {
                    if (qmap.getJSONArray("children") != null) {
                        for (JSONObject optionMap : qmap.getJSONArray("children").toJavaList(JSONObject.class)) {
                            if (StringUtils.isNotEmpty(optionMap.getString("composeGid"))) {
                                String[] gidList = Optional.ofNullable(optionMap.getString("gid")).orElse("")
                                        .split(",");
                                for (String gid : gidList) {
                                    String[] gids = gid.split("_");
                                    if (!composeGidDict.containsKey(gids[0])) {
                                        composeGidDict.put(gids[0], new JSONArray());
                                    }
                                    if (!composeGidDict.getJSONArray(gids[0]).contains(Integer.parseInt(gids[1]))) {
                                        // 前端要求compose_gid_dict中的gid对应的列表里面的元素改为int类型
                                        composeGidDict.getJSONArray(gids[0]).add(Integer.parseInt(gids[1]));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return composeGidDict;
    }

    private void formatOptionDict(JSONObject quotaGidOidDict, List<Integer> newList, String qgid) {
        // 更新quota_gid_oid_dict
        //    Args:
        //        quota_gid_oid_dict: 配额用到的题目gid对应的选项gid
        //        new_list: 新的选项gid
        //        q_gid: 问题gid
        List<Integer> oldOidList = Optional.ofNullable(quotaGidOidDict.getJSONArray(qgid))
                .orElse(new JSONArray()).toJavaList(Integer.class);
        if (CollectionUtils.isNotEmpty(oldOidList)) {
            oldOidList.addAll(newList);
        } else {
            oldOidList.addAll(newList);
        }
        quotaGidOidDict.put(qgid, oldOidList);
    }

    private List<Integer> regenOptionList(List<Integer> optionList, JSONObject ogidMap) {
        List<Integer> newOgidList = new ArrayList<>();
        optionList.forEach(ogid -> {
            Integer newOgid = ogidMap.getInteger(String.valueOf(ogid));
            if (newOgid != null) {
                newOgidList.add(newOgid);
            }
        });
        return newOgidList;
    }

    private String regenCheckpoints(List<String> qgidList, String checkpoints, JSONObject data, JSONObject qgidMap) {
        // 原来的配额检查点的题目假如在复制的题目中 则用原来的配额检查点，不然就新增配额检查点
        // 按照原来的题目顺序排序qgid_list，取最后一个题目生成配额检查点
        List<String> srcQgidList = data.getString("qgid_list") == null ? new ArrayList<>()
                : JSONObject.parseArray(data.getString("qgid_list"), String.class);
        // 原来的配额检查点的题目假如在复制的题目中 则用原来的配额检查点，不然就新增配额检查点
        List<String> checkpointIntersection = new ArrayList<>();
        List<JSONObject> checkpointList = org.apache.commons.lang3.StringUtils.isEmpty(checkpoints) ? new ArrayList<>()
                : JSONObject.parseArray(checkpoints, JSONObject.class);
        checkpointList.forEach(ckp -> {
            if (srcQgidList.contains(ckp.getString("gid"))) {
                checkpointIntersection.add(ckp.getString("gid"));
            }
        });
        if (CollectionUtils.isNotEmpty(checkpointIntersection)) {
            List<JSONObject> newCheckpoints = new ArrayList<>();
            for (String qgid : checkpointIntersection) {
                JSONObject qJs = new JSONObject();
                qJs.put("gid", qgidMap.getOrDefault(qgid, ""));
                qJs.put("cid", ((JSONObject) ((JSONObject) data.getOrDefault("qtitle_map", new JSONObject()))
                        .getOrDefault(qgid, new JSONObject())).get("cid"));
                qJs.put("title", parseHtmlStr(((JSONObject) ((JSONObject) data.getOrDefault("qtitle_map", new JSONObject()))
                        .getOrDefault(qgid, new JSONObject())).getString("title")));
                newCheckpoints.add(qJs);
            }
            return JSONObject.toJSONString(newCheckpoints);
        } else {
            // 配额中的最后一题
            // python中就是正序，不需要再排序
            List<JSONObject> newCheckpoints = new ArrayList<>();
            JSONObject qJs = new JSONObject();
            qJs.put("gid", qgidMap.getOrDefault(qgidList.get(qgidList.size() - 1), ""));
            qJs.put("cid", ((JSONObject) ((JSONObject) data.getOrDefault("qtitle_map", new JSONObject()))
                    .getOrDefault(qgidList.get(qgidList.size() - 1), new JSONObject())).get("cid"));
            qJs.put("title", parseHtmlStr(((JSONObject) ((JSONObject) data.getOrDefault("qtitle_map", new JSONObject()))
                    .getOrDefault(qgidList.get(qgidList.size() - 1), new JSONObject())).getString("title")));
            newCheckpoints.add(qJs);
            return JSONObject.toJSONString(newCheckpoints);
        }
    }

    // 解析html字符串
    private String parseHtmlStr(String htmlStr) {
        if (StringUtils.isEmpty(htmlStr)) {
            return "";
        }
        return HtmlUtil.cleanHtmlTag(htmlStr);
    }

    private List<String> regeQgidList(List<String> qgidList, JSONObject qgidMap) {
        // 重新生成 row，普通配额为[]，交叉配额有值
        List<String> newQgidList = new ArrayList<>();
        if (CollectionUtils.isEmpty(qgidList)) {
            return newQgidList;
        }
        for (String qgid : qgidList) {
            String newQgid = qgidMap.getString(qgid);
            if (StringUtils.isNotEmpty(newQgid)) {
                newQgidList.add(newQgid);
            }
        }
        return newQgidList;
    }

    private String regenOptionCombies(String optionCombiesStr, JSONObject qgidMap, JSONObject ogidMap) {
        List<String> optionCombies = StringUtils.isEmpty(optionCombiesStr) ? new ArrayList<>()
                : JSONObject.parseArray(optionCombiesStr, String.class);
        List<String> newOptionCombies = new ArrayList<>();
        optionCombies.forEach(optionCombie -> {
            List<String> options = Arrays.asList(optionCombie.split("\\|"));
            List<String> newOptionCombie = new ArrayList<>();
            options.forEach(option -> {
                String[] tmpList = option.split("_");
                String targetQgid = tmpList[0];
                String targetOgid = tmpList.length > 1 ? tmpList[1] : "";
                List<String> aList = new ArrayList<>();
                aList.add(Optional.ofNullable(qgidMap.getString(targetQgid)).orElse(""));
                aList.add(Optional.ofNullable(ogidMap.getString(targetOgid)).orElse(""));
                newOptionCombie.add(String.join("_", aList));
            });
            newOptionCombies.add(String.join("|", newOptionCombie));
        });
        return JSONObject.toJSONString(newOptionCombies);
    }

    private SurveyQuotaConditionVo setSurveyQuotaConditionVo(SurveyQuotaCondition surveyQuotaCondition, boolean isFront) {
        if (surveyQuotaCondition == null) {
            return new SurveyQuotaConditionVo();
        }
        SurveyQuotaConditionVo surveyQuotaConditionVo = surveyConvertMapper.condition2Vo(surveyQuotaCondition);
        LambdaQueryWrapper<SurveyQuotaConditionQgidList> defaultWrapper = Wrappers.lambdaQuery();
        defaultWrapper.eq(SurveyQuotaConditionQgidList::getQuotaCondId, surveyQuotaCondition.getId());
        List<SurveyQuotaConditionQgidList> quotaConditionQgidLists = surveyQuotaConditionQgidListMapper.selectList(defaultWrapper);
        surveyQuotaConditionVo.setQgidList(quotaConditionQgidLists);

        LambdaQueryWrapper<SurveyQuotaCheckItem> itemWrapper = Wrappers.lambdaQuery();
        itemWrapper.eq(SurveyQuotaCheckItem::getQuotaCondId, surveyQuotaCondition.getId());
        List<SurveyQuotaCheckItem> quotaCheckItems = surveyQuotaCheckItemMapper.selectList(itemWrapper);
        if (CollectionUtils.isNotEmpty(quotaCheckItems)) {
            List<SurveyQuotaCheckItemVo> surveyQuotaCheckItemVos = surveyConvertMapper.checkItem2Vos(quotaCheckItems);
            surveyQuotaCheckItemVos.forEach(quotaCheckItem -> {
                LambdaQueryWrapper<SurveyQuotaCheckItemOptionList> itemOptionWrapper = Wrappers.lambdaQuery();
                itemOptionWrapper.eq(SurveyQuotaCheckItemOptionList::getQuotaCheckitemId, quotaCheckItem.getId());
                List<SurveyQuotaCheckItemOptionList> quotaCheckItemOptionLists = surveyQuotaCheckItemOptionListMapper
                        .selectList(itemOptionWrapper);
                quotaCheckItem.setOptionList(quotaCheckItemOptionLists);
                LambdaQueryWrapper<SurveyQuotaCheckItemOptionCombies> combiesWrapper = Wrappers.lambdaQuery();
                combiesWrapper.eq(SurveyQuotaCheckItemOptionCombies::getQuotaCheckitemId, quotaCheckItem.getId());
                List<SurveyQuotaCheckItemOptionCombies> quotaCheckItemOptionCombies =
                        surveyQuotaCheckItemOptionCombiesMapper.selectList(combiesWrapper);
                quotaCheckItem.setQuotaCheckitemList(quotaCheckItemOptionCombies);
            });
            surveyQuotaConditionVo.setQuotaCheckitemList(surveyQuotaCheckItemVos);
        }
        if (isFront) {
            itemToFront(surveyQuotaConditionVo);
        }
        return surveyQuotaConditionVo;
    }

    private void itemToFront(SurveyQuotaConditionVo surveyQuotaConditionVo) {
        List<SurveyQuotaCheckItemVo> qciList = surveyQuotaConditionVo.getQuotaCheckitemList();
        if (CollectionUtils.isNotEmpty(qciList)) {
            SurveyQuotaCheckItemVo quotaCheckItemVo = qciList.get(0);
            surveyQuotaConditionVo.setNum(quotaCheckItemVo.getNum());
            surveyQuotaConditionVo.setOptionList(quotaCheckItemVo.getOptionList());
        }
        replaceCheckPointTitle(surveyQuotaConditionVo);
    }

    private void replaceCheckPointTitle(SurveyQuotaConditionVo surveyQuotaConditionVo) {
        // 替换checkpoint 中的题目title 为最新题目title
        if (surveyQuotaConditionVo == null) {
            return;
        }
        String checkpoints = surveyQuotaConditionVo.getCheckpoints();
        if (StringUtils.isNotEmpty(checkpoints)) {
            JSONArray checkpointJs = JSONArray.parseArray(checkpoints);
            Project project = qdesGrpcClient.getProject(surveyQuotaConditionVo.getProjectId());
            Map<String, Question> questionJs = new HashMap<>();
            List<Question> questionList = qdesGrpcClient.getQuestionList(project.getStructId());
            questionList.forEach(question -> questionJs.put(String.valueOf(question.getGid()), question));
            for (int i = 0; i < checkpointJs.size(); i++) {
                JSONObject onePoint = checkpointJs.getJSONObject(i);
                if (StringUtils.isNotEmpty(onePoint.getString("gid"))) {
                    Question q = questionJs.get(onePoint.getString("gid"));
                    if (q != null) {
                        JSONObject dataJs = JSONObject.parseObject(q.getData());
                        if (dataJs != null) {
                            onePoint.put("title", HtmlUtil.cleanHtmlTag(dataJs.getString("title")));
                        }
                    }
                }
            }
            surveyQuotaConditionVo.setCheckpoints(checkpointJs.toJSONString());
        }
    }

    private Map<Integer, SurveyQuotaCondition> handle(List<Integer> list1, List<SurveyQuotaCondition> list2) {
        return IntStream.range(0, list1.size()).collect(HashMap::new, (m, i) -> m.put(list1.get(i), list2.get(i)), (m, n) -> {
        });
    }
}